From ed63954c6aa76a6451ffdc14990466e402e71c5f Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 28 Mar 2025 15:30:33 +0200 Subject: [PATCH 001/100] added external_service, configured_addon, authorized_account --- addon_service/admin/__init__.py | 28 +++++- .../authorized_account/link/__init__.py | 0 .../authorized_account/link/models.py | 24 +++++ .../authorized_account/link/serializers.py | 86 +++++++++++++++++ .../authorized_account/link/views.py | 9 ++ addon_service/common/known_imps.py | 19 ++++ addon_service/common/validators.py | 5 + .../configured_addon/link/__init__.py | 0 addon_service/configured_addon/link/models.py | 18 ++++ .../configured_addon/link/serializers.py | 78 +++++++++++++++ addon_service/configured_addon/link/views.py | 14 +++ .../external_service/link/__init__.py | 2 + addon_service/external_service/link/models.py | 65 +++++++++++++ .../external_service/link/serializers.py | 48 ++++++++++ addon_service/external_service/link/views.py | 9 ++ ...inkaccount_configuredlinkaddon_and_more.py | 94 +++++++++++++++++++ addon_service/models.py | 6 ++ 17 files changed, 503 insertions(+), 2 deletions(-) create mode 100644 addon_service/authorized_account/link/__init__.py create mode 100644 addon_service/authorized_account/link/models.py create mode 100644 addon_service/authorized_account/link/serializers.py create mode 100644 addon_service/authorized_account/link/views.py create mode 100644 addon_service/configured_addon/link/__init__.py create mode 100644 addon_service/configured_addon/link/models.py create mode 100644 addon_service/configured_addon/link/serializers.py create mode 100644 addon_service/configured_addon/link/views.py create mode 100644 addon_service/external_service/link/__init__.py create mode 100644 addon_service/external_service/link/models.py create mode 100644 addon_service/external_service/link/serializers.py create mode 100644 addon_service/external_service/link/views.py create mode 100644 addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py diff --git a/addon_service/admin/__init__.py b/addon_service/admin/__init__.py index 45eb02b6..e06f11b1 100644 --- a/addon_service/admin/__init__.py +++ b/addon_service/admin/__init__.py @@ -8,6 +8,7 @@ from addon_service.external_service.storage.models import StorageSupportedFeatures from ..external_service.citation.models import CitationSupportedFeatures +from ..external_service.link.models import SupportedResourceTypes from ._base import GravyvaletModelAdmin from .decorators import linked_many_field @@ -22,7 +23,7 @@ class ExternalStorageServiceAdmin(GravyvaletModelAdmin): ) raw_id_fields = ("oauth2_client_config", "oauth1_client_config") enum_choice_fields = { - "int_addon_imp": known_imps.AddonImpNumbers, + "int_addon_imp": known_imps.StorageAddonImpNumbers, "int_credentials_format": CredentialsFormats, "int_service_type": ServiceTypes, } @@ -41,7 +42,7 @@ class ExternalCitationServiceAdmin(GravyvaletModelAdmin): ) raw_id_fields = ("oauth2_client_config", "oauth1_client_config") enum_choice_fields = { - "int_addon_imp": known_imps.AddonImpNumbers, + "int_addon_imp": known_imps.CitationAddonImpNumbers, "int_credentials_format": CredentialsFormats, "int_service_type": ServiceTypes, } @@ -50,6 +51,25 @@ class ExternalCitationServiceAdmin(GravyvaletModelAdmin): } +@admin.register(models.ExternalLinkService) +class ExternalLinkServiceAdmin(GravyvaletModelAdmin): + list_display = ("display_name", "created", "modified") + readonly_fields = ( + "id", + "created", + "modified", + ) + raw_id_fields = ("oauth2_client_config", "oauth1_client_config") + enum_choice_fields = { + "int_addon_imp": known_imps.AddonImpNumbers, + "int_credentials_format": CredentialsFormats, + "int_service_type": ServiceTypes, + } + enum_multiple_choice_fields = { + "int_supported_resource_types": SupportedResourceTypes, + } + + @admin.register(models.ExternalComputingService) class ExternalComputingServiceAdmin(GravyvaletModelAdmin): list_display = ("display_name", "created", "modified") @@ -72,6 +92,8 @@ class ExternalComputingServiceAdmin(GravyvaletModelAdmin): @admin.register(models.OAuth2ClientConfig) @linked_many_field("external_storage_services") @linked_many_field("external_citation_services") +@linked_many_field("external_link_services") +@linked_many_field("external_computing_services") class OAuth2ClientConfigAdmin(GravyvaletModelAdmin): readonly_fields = ( "id", @@ -83,6 +105,8 @@ class OAuth2ClientConfigAdmin(GravyvaletModelAdmin): @admin.register(models.OAuth1ClientConfig) @linked_many_field("external_storage_services") @linked_many_field("external_citation_services") +@linked_many_field("external_link_services") +@linked_many_field("external_computing_services") class OAuth1ClientConfigAdmin(GravyvaletModelAdmin): readonly_fields = ( "id", diff --git a/addon_service/authorized_account/link/__init__.py b/addon_service/authorized_account/link/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/addon_service/authorized_account/link/models.py b/addon_service/authorized_account/link/models.py new file mode 100644 index 00000000..d3598978 --- /dev/null +++ b/addon_service/authorized_account/link/models.py @@ -0,0 +1,24 @@ +from addon_service.authorized_account.models import AuthorizedAccount +from addon_service.configured_addon.storage.models import ConfiguredStorageAddon + + +class AuthorizedLinkAccount(AuthorizedAccount): + """Model for describing a user's account on an ExternalService. + + This model collects all of the information required to actually perform remote + operations against the service and to aggregate accounts under a known user. + """ + + class Meta: + verbose_name = "Authorized Link Account" + verbose_name_plural = "Authorized Link Accounts" + app_label = "addon_service" + + class JSONAPIMeta: + resource_name = "authorized-storage-accounts" + + @property + def configured_storage_addons(self): + return ConfiguredStorageAddon.objects.filter(base_account=self).select_related( + "authorized_resource" + ) diff --git a/addon_service/authorized_account/link/serializers.py b/addon_service/authorized_account/link/serializers.py new file mode 100644 index 00000000..0d8fbc6c --- /dev/null +++ b/addon_service/authorized_account/link/serializers.py @@ -0,0 +1,86 @@ +from rest_framework_json_api import serializers +from rest_framework_json_api.relations import ( + HyperlinkedRelatedField, + ResourceRelatedField, +) +from rest_framework_json_api.utils import get_resource_type_from_model + +from addon_service.addon_operation.models import AddonOperationModel +from addon_service.authorized_account.serializers import AuthorizedAccountSerializer +from addon_service.common import view_names +from addon_service.common.serializer_fields import ( + DataclassRelatedLinkField, + ReadOnlyResourceRelatedField, +) +from addon_service.models import ( + AuthorizedStorageAccount, + ConfiguredLinkAddon, + ExternalLinkService, + UserReference, +) + + +RESOURCE_TYPE = get_resource_type_from_model(AuthorizedStorageAccount) + + +class AuthorizedLinkAccountSerializer(AuthorizedAccountSerializer): + external_link_service = ResourceRelatedField( + queryset=ExternalLinkService.objects.all(), + many=False, + source="external_service.externallinkservice", + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + ) + configured_link_addons = HyperlinkedRelatedField( + many=True, + source="configured_addons", + queryset=ConfiguredLinkAddon.objects.active(), + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + required=False, + ) + url = serializers.HyperlinkedIdentityField( + view_name=view_names.detail_view(RESOURCE_TYPE), required=False + ) + account_owner = ReadOnlyResourceRelatedField( + many=False, + queryset=UserReference.objects.all(), + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + ) + authorized_operations = DataclassRelatedLinkField( + dataclass_model=AddonOperationModel, + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + ) + + included_serializers = { + "account_owner": "addon_service.serializers.UserReferenceSerializer", + "external_link_service": "addon_service.serializers.ExternalLinkServiceSerializer", + "configured_link_addons": "addon_service.serializers.ConfiguredLinkSerializer", + "authorized_operations": "addon_service.serializers.AddonOperationSerializer", + } + + configured_addons_uris = serializers.SerializerMethodField() + + def get_configured_addons_uris(self, obj): + return obj.configured_link_addons.values_list( + "authorized_resource__resource_uri", flat=True + ) + + class Meta: + model = AuthorizedStorageAccount + fields = [ + "id", + "url", + "display_name", + "account_owner", + "api_base_url", + "auth_url", + "authorized_capabilities", + "authorized_operations", + "authorized_operation_names", + "configured_link_addons", + "credentials", + "default_root_folder", + "external_link_service", + "initiate_oauth", + "credentials_available", + "configured_addons_uris", + ] diff --git a/addon_service/authorized_account/link/views.py b/addon_service/authorized_account/link/views.py new file mode 100644 index 00000000..c8d81e58 --- /dev/null +++ b/addon_service/authorized_account/link/views.py @@ -0,0 +1,9 @@ +from addon_service.authorized_account.views import AuthorizedAccountViewSet + +from .models import AuthorizedLinkAccount +from .serializers import AuthorizedLinkAccountSerializer + + +class AuthorizedStorageAccountViewSet(AuthorizedAccountViewSet): + queryset = AuthorizedLinkAccount.objects.all() + serializer_class = AuthorizedLinkAccountSerializer diff --git a/addon_service/common/known_imps.py b/addon_service/common/known_imps.py index 20e85b37..46f2955b 100644 --- a/addon_service/common/known_imps.py +++ b/addon_service/common/known_imps.py @@ -114,3 +114,22 @@ class AddonImpNumbers(enum.Enum): if __debug__: BLARG = -7 + + +StorageAddonImpNumbers = frozenset( + { + AddonImpNumbers.BOX, + AddonImpNumbers.S3, + AddonImpNumbers.GOOGLEDRIVE, + AddonImpNumbers.DROPBOX, + AddonImpNumbers.FIGSHARE, + AddonImpNumbers.ONEDRIVE, + AddonImpNumbers.OWNCLOUD, + AddonImpNumbers.DATAVERSE, + AddonImpNumbers.GITLAB, + AddonImpNumbers.BITBUCKET, + AddonImpNumbers.GITHUB, + } +) +CitationAddonImpNumbers = frozenset({AddonImpNumbers.ZOTERO, AddonImpNumbers.MENDELEY}) +ComputingAddonImpNumbers = frozenset({AddonImpNumbers.BOA}) diff --git a/addon_service/common/validators.py b/addon_service/common/validators.py index 550ee414..be760a95 100644 --- a/addon_service/common/validators.py +++ b/addon_service/common/validators.py @@ -10,6 +10,7 @@ from addon_toolkit import AddonCapabilities from addon_toolkit.interfaces.citation import CitationAddonImp from addon_toolkit.interfaces.computing import ComputingAddonImp +from addon_toolkit.interfaces.link import LinkAddonImp from addon_toolkit.interfaces.storage import StorageAddonImp from . import known_imps @@ -59,6 +60,10 @@ def validate_storage_imp_number(value): _validate_imp_number(value, StorageAddonImp) +def validate_link_imp_number(value): + _validate_imp_number(value, LinkAddonImp) + + def validate_citation_imp_number(value): _validate_imp_number(value, CitationAddonImp) diff --git a/addon_service/configured_addon/link/__init__.py b/addon_service/configured_addon/link/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py new file mode 100644 index 00000000..e42ae069 --- /dev/null +++ b/addon_service/configured_addon/link/models.py @@ -0,0 +1,18 @@ +from django.db import models + +from addon_service.configured_addon.models import ConfiguredAddon + + +class ConfiguredLinkAddon(ConfiguredAddon): + + target_uri = models.URLField() + target_id = models.CharField() + int_resource_type = models.IntegerField() + + class Meta: + verbose_name = "Configured Link Addon" + verbose_name_plural = "Configured Link Addons" + app_label = "addon_service" + + class JSONAPIMeta: + resource_name = "configured-link-addons" diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py new file mode 100644 index 00000000..db55a035 --- /dev/null +++ b/addon_service/configured_addon/link/serializers.py @@ -0,0 +1,78 @@ +from rest_framework.fields import ( + CharField, + IntegerField, + URLField, +) +from rest_framework_json_api.relations import ResourceRelatedField +from rest_framework_json_api.utils import get_resource_type_from_model + +from addon_service.addon_operation.models import AddonOperationModel +from addon_service.common import view_names +from addon_service.common.serializer_fields import DataclassRelatedLinkField +from addon_service.configured_addon.serializers import ConfiguredAddonSerializer +from addon_service.external_service.link.models import ExternalLinkService +from addon_service.models import ( + AuthorizedLinkAccount, + ConfiguredLinkAddon, +) + + +RESOURCE_TYPE = get_resource_type_from_model(ConfiguredLinkAddon) + + +class ConfiguredLinkAddonSerializer(ConfiguredAddonSerializer): + """api serializer for the `ConfiguredLinkAddon` model""" + + target_uri = URLField() + target_id = CharField() + int_resource_type = IntegerField() + + connected_operations = DataclassRelatedLinkField( + dataclass_model=AddonOperationModel, + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + read_only=True, + ) + base_account = ResourceRelatedField( + queryset=AuthorizedLinkAccount.objects.all(), + many=False, + source="base_account.authorizedlinkaccount", + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + ) + external_link_service = ResourceRelatedField( + many=False, + read_only=True, + model=ExternalLinkService, + source="base_account.external_service.externallinkservice", + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + ) + authorized_resource = ResourceRelatedField( + many=False, + read_only=True, + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + ) + + included_serializers = { + "base_account": ("addon_service.serializers.AuthorizedLinkAccountSerializer"), + "external_link_service": "addon_service.serializers.ExternalLinkServiceSerializer", + "authorized_resource": "addon_service.serializers.ResourceReferenceSerializer", + "connected_operations": "addon_service.serializers.AddonOperationSerializer", + } + + class Meta: + model = ConfiguredLinkAddon + read_only_fields = ["external_link_service"] + fields = [ + "id", + "url", + "display_name", + "root_folder", + "base_account", + "authorized_resource", + "authorized_resource_uri", + "connected_capabilities", + "connected_operations", + "connected_operation_names", + "external_link_service", + "current_user_is_owner", + "external_service_name", + ] diff --git a/addon_service/configured_addon/link/views.py b/addon_service/configured_addon/link/views.py new file mode 100644 index 00000000..ecadab51 --- /dev/null +++ b/addon_service/configured_addon/link/views.py @@ -0,0 +1,14 @@ +from addon_service.configured_addon.views import ConfiguredAddonViewSet + +from .models import ConfiguredLinkAddon +from .serializers import ConfiguredLinkAddonSerializer + + +class ConfiguredLinkAddonViewSet(ConfiguredAddonViewSet): + queryset = ConfiguredLinkAddon.objects.active().select_related( + "base_account__authorizedlinkaccount", + "base_account__account_owner", + "base_account__external_service__externallinkservice", + "authorized_resource", + ) + serializer_class = ConfiguredLinkAddonSerializer diff --git a/addon_service/external_service/link/__init__.py b/addon_service/external_service/link/__init__.py new file mode 100644 index 00000000..6a071ca7 --- /dev/null +++ b/addon_service/external_service/link/__init__.py @@ -0,0 +1,2 @@ +"""addon_service.external_service.storage: represents a third-party storage service +""" diff --git a/addon_service/external_service/link/models.py b/addon_service/external_service/link/models.py new file mode 100644 index 00000000..104549c4 --- /dev/null +++ b/addon_service/external_service/link/models.py @@ -0,0 +1,65 @@ +from enum import ( + Flag, + auto, +) + +from django.db import models + +from addon_service.authorized_account.link.models import AuthorizedLinkAccount +from addon_service.common.validators import ( + _validate_enum_value, + validate_link_imp_number, +) +from addon_service.external_service.models import ExternalService + + +class SupportedResourceTypes(Flag): + ADD_UPDATE_FILES = auto() + ADD_UPDATE_FILES_PARTIAL = auto() + DELETE_FILES = auto() + DELETE_FILES_PARTIAL = auto() + FORKING = auto() + LOGS = auto() + PERMISSIONS = auto() + REGISTERING = auto() + FILE_VERSIONS = auto() + COPY_INTO = auto() + DOWNLOAD_AS_ZIP = auto() + + +def validate_supported_features(value): + _validate_enum_value(SupportedResourceTypes, value) + + +class ExternalLinkService(ExternalService): + int_supported_resource_types = models.IntegerField( + validators=[validate_supported_features], null=True + ) + + @property + def supported_resource_types(self) -> list[SupportedResourceTypes]: + """get the enum representation of int_supported_features""" + return SupportedResourceTypes(self.int_supported_features) + + @supported_resource_types.setter + def supported_resource_types( + self, new_supported_resource_types: SupportedResourceTypes + ): + """set int_authorized_capabilities without caring its int""" + self.int_supported_features = new_supported_resource_types.value + + def clean(self): + super().clean() + validate_link_imp_number(self.int_addon_imp) + + @property + def authorized_link_accounts(self): + return AuthorizedLinkAccount.objects.filter(external_service=self) + + class Meta: + verbose_name = "External Link Service" + verbose_name_plural = "External Link Services" + app_label = "addon_service" + + class JSONAPIMeta: + resource_name = "external-link-services" diff --git a/addon_service/external_service/link/serializers.py b/addon_service/external_service/link/serializers.py new file mode 100644 index 00000000..556a3175 --- /dev/null +++ b/addon_service/external_service/link/serializers.py @@ -0,0 +1,48 @@ +from rest_framework_json_api import serializers +from rest_framework_json_api.utils import get_resource_type_from_model + +from addon_service.addon_imp.models import AddonImpModel +from addon_service.common import view_names +from addon_service.common.enum_serializers import EnumNameMultipleChoiceField +from addon_service.common.serializer_fields import DataclassRelatedDataField +from addon_service.external_service.link.models import SupportedResourceTypes +from addon_service.external_service.serializers import ExternalServiceSerializer + +from .models import ExternalLinkService + + +RESOURCE_TYPE = get_resource_type_from_model(ExternalLinkService) + + +class ExternalLinkServiceSerializer(ExternalServiceSerializer): + """api serializer for the `ExternalService` model""" + + url = serializers.HyperlinkedIdentityField( + view_name=view_names.detail_view(RESOURCE_TYPE) + ) + + addon_imp = DataclassRelatedDataField( + dataclass_model=AddonImpModel, + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + ) + + supported_resource_types = EnumNameMultipleChoiceField( + enum_cls=SupportedResourceTypes, read_only=True + ) + + class Meta: + model = ExternalLinkService + fields = [ + "id", + "addon_imp", + "auth_uri", + "credentials_format", + "display_name", + "url", + "wb_key", + "external_service_name", + "configurable_api_root", + "supported_resource_types", + "icon_url", + "api_base_url_options", + ] diff --git a/addon_service/external_service/link/views.py b/addon_service/external_service/link/views.py new file mode 100644 index 00000000..6eccaf8d --- /dev/null +++ b/addon_service/external_service/link/views.py @@ -0,0 +1,9 @@ +from rest_framework_json_api.views import ReadOnlyModelViewSet + +from .models import ExternalLinkService +from .serializers import ExternalLinkServiceSerializer + + +class ExternalLinkServiceViewSet(ReadOnlyModelViewSet): + queryset = ExternalLinkService.objects.all().select_related("oauth2_client_config") + serializer_class = ExternalLinkServiceSerializer diff --git a/addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py b/addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py new file mode 100644 index 00000000..707c5f06 --- /dev/null +++ b/addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py @@ -0,0 +1,94 @@ +# Generated by Django 4.2.7 on 2025-03-28 12:06 + +import django.db.models.deletion +from django.db import ( + migrations, + models, +) + +import addon_service.external_service.link.models + + +class Migration(migrations.Migration): + + dependencies = [ + ("addon_service", "0011_oauth2tokenmetadata_date_last_refreshed"), + ] + + operations = [ + migrations.CreateModel( + name="AuthorizedLinkAccount", + fields=[ + ( + "authorizedaccount_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="addon_service.authorizedaccount", + ), + ), + ], + options={ + "verbose_name": "Authorized Link Account", + "verbose_name_plural": "Authorized Link Accounts", + }, + bases=("addon_service.authorizedaccount",), + ), + migrations.CreateModel( + name="ConfiguredLinkAddon", + fields=[ + ( + "configuredaddon_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="addon_service.configuredaddon", + ), + ), + ("target_uri", models.URLField()), + ("target_id", models.CharField()), + ("int_resource_type", models.IntegerField()), + ], + options={ + "verbose_name": "Configured Link Addon", + "verbose_name_plural": "Configured Link Addons", + }, + bases=("addon_service.configuredaddon",), + ), + migrations.CreateModel( + name="ExternalLinkService", + fields=[ + ( + "externalservice_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="addon_service.externalservice", + ), + ), + ( + "int_supported_resource_types", + models.IntegerField( + null=True, + validators=[ + addon_service.external_service.link.models.validate_supported_features + ], + ), + ), + ], + options={ + "verbose_name": "External Storage Service", + "verbose_name_plural": "External Storage Services", + }, + bases=("addon_service.externalservice",), + ), + ] diff --git a/addon_service/models.py b/addon_service/models.py index 99e0395e..b350e261 100644 --- a/addon_service/models.py +++ b/addon_service/models.py @@ -5,13 +5,16 @@ from addon_service.addon_operation_invocation.models import AddonOperationInvocation from addon_service.authorized_account.citation.models import AuthorizedCitationAccount from addon_service.authorized_account.computing.models import AuthorizedComputingAccount +from addon_service.authorized_account.link.models import AuthorizedLinkAccount from addon_service.authorized_account.storage.models import AuthorizedStorageAccount from addon_service.configured_addon.citation.models import ConfiguredCitationAddon from addon_service.configured_addon.computing.models import ConfiguredComputingAddon +from addon_service.configured_addon.link.models import ConfiguredLinkAddon from addon_service.configured_addon.storage.models import ConfiguredStorageAddon from addon_service.credentials.models import ExternalCredentials from addon_service.external_service.citation.models import ExternalCitationService from addon_service.external_service.computing.models import ExternalComputingService +from addon_service.external_service.link.models import ExternalLinkService from addon_service.external_service.storage.models import ExternalStorageService from addon_service.oauth1.models import OAuth1ClientConfig from addon_service.oauth2.models import ( @@ -41,4 +44,7 @@ "AuthorizedComputingAccount", "ConfiguredComputingAddon", "ExternalComputingService", + "ExternalLinkService", + "AuthorizedLinkAccount", + "ConfiguredLinkAddon", ) From 535639aae46a490357d42b027c48805cef39048c Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 28 Mar 2025 15:49:34 +0200 Subject: [PATCH 002/100] fixed leftovers --- addon_service/admin/_base.py | 10 +- .../authorized_account/link/models.py | 8 +- addon_service/common/known_imps.py | 2 +- .../external_service/link/__init__.py | 2 +- addon_toolkit/interfaces/link.py | 141 ++++++++++++++++++ 5 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 addon_toolkit/interfaces/link.py diff --git a/addon_service/admin/_base.py b/addon_service/admin/_base.py index a076e2f0..5df1fe84 100644 --- a/addon_service/admin/_base.py +++ b/addon_service/admin/_base.py @@ -56,10 +56,7 @@ def formfield_for_dbfield(self, db_field, request, **kwargs): label=db_field.verbose_name, choices=[ (None, ""), - *( - (_member.value, _member.name) - for _member in _enum.__members__.values() - ), + *((_member.value, _member.name) for _member in _enum), ], ) if ( @@ -69,10 +66,7 @@ def formfield_for_dbfield(self, db_field, request, **kwargs): _enum = self.enum_multiple_choice_fields[db_field.name] return EnumNameMultipleChoiceField( choices=[ - *( - (_member.value, _member.name) - for _member in _enum.__members__.values() - ), + *((_member.value, _member.name) for _member in _enum), ], widget=forms.CheckboxSelectMultiple, enum_cls=_enum, diff --git a/addon_service/authorized_account/link/models.py b/addon_service/authorized_account/link/models.py index d3598978..615d95ae 100644 --- a/addon_service/authorized_account/link/models.py +++ b/addon_service/authorized_account/link/models.py @@ -1,5 +1,5 @@ from addon_service.authorized_account.models import AuthorizedAccount -from addon_service.configured_addon.storage.models import ConfiguredStorageAddon +from addon_service.configured_addon.link.models import ConfiguredLinkAddon class AuthorizedLinkAccount(AuthorizedAccount): @@ -15,10 +15,10 @@ class Meta: app_label = "addon_service" class JSONAPIMeta: - resource_name = "authorized-storage-accounts" + resource_name = "authorized-link-accounts" @property - def configured_storage_addons(self): - return ConfiguredStorageAddon.objects.filter(base_account=self).select_related( + def configured_link_addons(self): + return ConfiguredLinkAddon.objects.filter(base_account=self).select_related( "authorized_resource" ) diff --git a/addon_service/common/known_imps.py b/addon_service/common/known_imps.py index 46f2955b..7557f953 100644 --- a/addon_service/common/known_imps.py +++ b/addon_service/common/known_imps.py @@ -90,8 +90,8 @@ class KnownAddonImps(enum.Enum): BLARG = my_blarg.MyBlargStorage -@enum.unique @enum_names_same_as(KnownAddonImps) +@enum.unique class AddonImpNumbers(enum.Enum): """Static mapping from each AddonImp name to a unique integer (for database use)""" diff --git a/addon_service/external_service/link/__init__.py b/addon_service/external_service/link/__init__.py index 6a071ca7..4754ef53 100644 --- a/addon_service/external_service/link/__init__.py +++ b/addon_service/external_service/link/__init__.py @@ -1,2 +1,2 @@ -"""addon_service.external_service.storage: represents a third-party storage service +"""addon_service.external_service.link: represents a third-party link service """ diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py new file mode 100644 index 00000000..3436e7ed --- /dev/null +++ b/addon_toolkit/interfaces/link.py @@ -0,0 +1,141 @@ +"""a static (and still in progress) definition of what composes a link addon""" + +import dataclasses +import enum +import typing +from collections import abc + +from addon_toolkit.addon_operation_declaration import immediate_operation +from addon_toolkit.capabilities import AddonCapabilities +from addon_toolkit.constrained_network.http import HttpRequestor +from addon_toolkit.credentials import Credentials +from addon_toolkit.cursor import Cursor +from addon_toolkit.imp import AddonImp + +from ._base import BaseAddonInterface + + +__all__ = ( + "ItemResult", + "ItemType", + "ItemSampleResult", + "PossibleSingleItemResult", + "LinkAddonInterface", + "LinkAddonImp", + "LinkConfig", +) + + +### +# dataclasses used for operation args and return values + + +@dataclasses.dataclass(frozen=True) +class LinkConfig: + max_upload_mb: int + external_api_url: str + connected_root_id: str | None = None + external_account_id: str | None = None + + +class ItemType(enum.StrEnum): + FILE = enum.auto() + FOLDER = enum.auto() + + +@dataclasses.dataclass +class ItemResult: + item_id: str + item_name: str + item_type: ItemType + can_be_root: bool = None + may_contain_root_candidates: bool = None + item_path: abc.Sequence[typing.Self] | None = None + + def __post_init__(self): + """By default can_be_root and may_contain_root_candidates are bound to item_type""" + if self.can_be_root is None: + self.can_be_root = self.item_type == ItemType.FOLDER + if self.may_contain_root_candidates is None: + self.may_contain_root_candidates = self.item_type == ItemType.FOLDER + + +@dataclasses.dataclass +class PossibleSingleItemResult: + possible_item: ItemResult | None + + +@dataclasses.dataclass +class ItemSampleResult: + """a sample from a possibly-large population of result items""" + + items: abc.Collection[ItemResult] + total_count: int | None = None + this_sample_cursor: str = "" + next_sample_cursor: str | None = None # when None, this is the last page of results + prev_sample_cursor: str | None = None + first_sample_cursor: str = "" + + def with_cursor(self, cursor: Cursor) -> typing.Self: + return dataclasses.replace( + self, + this_sample_cursor=cursor.this_cursor_str, + next_sample_cursor=cursor.next_cursor_str, + prev_sample_cursor=cursor.prev_cursor_str, + first_sample_cursor=cursor.first_cursor_str, + ) + + +### +# declaration of all link addon operations + + +class LinkAddonInterface(BaseAddonInterface, typing.Protocol): + + @immediate_operation(capability=AddonCapabilities.ACCESS) + async def get_item_info(self, item_id: str) -> ItemResult: ... + + @immediate_operation(capability=AddonCapabilities.ACCESS) + async def list_root_items(self, page_cursor: str = "") -> ItemSampleResult: ... + + @immediate_operation(capability=AddonCapabilities.ACCESS) + async def list_child_items( + self, + item_id: str, + page_cursor: str = "", + item_type: ItemType | None = None, + ) -> ItemSampleResult: ... + + +@dataclasses.dataclass +class LinkAddonImp(AddonImp): + """base class for link addon implementations""" + + ADDON_INTERFACE = LinkAddonInterface + + config: LinkConfig + + async def build_wb_config(self) -> dict: + return {} + + +@dataclasses.dataclass +class LinkAddonHttpRequestorImp(LinkAddonImp): + """base class for link addon implementations using GV network""" + + network: HttpRequestor + + +@dataclasses.dataclass +class LinkAddonClientRequestorImp[T](LinkAddonImp): + """base class for link addon with custom clients""" + + client: T = dataclasses.field(init=False) + credentials: dataclasses.InitVar[Credentials] + + def __post_init__(self, credentials): + self.client = self.create_client(credentials) + + @staticmethod + def create_client(credentials) -> T: + raise NotImplementedError From f0fcddded39c3495f235a8889de645e8779cf1ee Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 28 Mar 2025 15:55:09 +0200 Subject: [PATCH 003/100] cleaned up interface for link addons --- addon_toolkit/interfaces/link.py | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py index 3436e7ed..4f72b651 100644 --- a/addon_toolkit/interfaces/link.py +++ b/addon_toolkit/interfaces/link.py @@ -19,25 +19,11 @@ "ItemResult", "ItemType", "ItemSampleResult", - "PossibleSingleItemResult", "LinkAddonInterface", "LinkAddonImp", - "LinkConfig", ) -### -# dataclasses used for operation args and return values - - -@dataclasses.dataclass(frozen=True) -class LinkConfig: - max_upload_mb: int - external_api_url: str - connected_root_id: str | None = None - external_account_id: str | None = None - - class ItemType(enum.StrEnum): FILE = enum.auto() FOLDER = enum.auto() @@ -48,21 +34,7 @@ class ItemResult: item_id: str item_name: str item_type: ItemType - can_be_root: bool = None - may_contain_root_candidates: bool = None - item_path: abc.Sequence[typing.Self] | None = None - - def __post_init__(self): - """By default can_be_root and may_contain_root_candidates are bound to item_type""" - if self.can_be_root is None: - self.can_be_root = self.item_type == ItemType.FOLDER - if self.may_contain_root_candidates is None: - self.may_contain_root_candidates = self.item_type == ItemType.FOLDER - - -@dataclasses.dataclass -class PossibleSingleItemResult: - possible_item: ItemResult | None + item_link: str | None = None @dataclasses.dataclass @@ -113,8 +85,6 @@ class LinkAddonImp(AddonImp): ADDON_INTERFACE = LinkAddonInterface - config: LinkConfig - async def build_wb_config(self) -> dict: return {} From 1daba47b06d853a88496c8b9ad5cff9159c1af62 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 28 Mar 2025 15:57:39 +0200 Subject: [PATCH 004/100] added resource type --- addon_toolkit/interfaces/link.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py index 4f72b651..85c5469a 100644 --- a/addon_toolkit/interfaces/link.py +++ b/addon_toolkit/interfaces/link.py @@ -1,5 +1,7 @@ """a static (and still in progress) definition of what composes a link addon""" +from __future__ import annotations + import dataclasses import enum import typing @@ -15,6 +17,9 @@ from ._base import BaseAddonInterface +if typing.TYPE_CHECKING: + from addon_service.external_service.link.models import SupportedResourceTypes + __all__ = ( "ItemResult", "ItemType", @@ -34,6 +39,7 @@ class ItemResult: item_id: str item_name: str item_type: ItemType + resource_type: SupportedResourceTypes item_link: str | None = None From 52995450d4d636c678179762bf097f9627bf5971 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 31 Mar 2025 13:23:19 +0300 Subject: [PATCH 005/100] fixed supportedFeatures enum --- addon_service/external_service/link/models.py | 20 +------------------ addon_toolkit/interfaces/link.py | 19 +++++++++++++----- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/addon_service/external_service/link/models.py b/addon_service/external_service/link/models.py index 104549c4..41c819ad 100644 --- a/addon_service/external_service/link/models.py +++ b/addon_service/external_service/link/models.py @@ -1,8 +1,3 @@ -from enum import ( - Flag, - auto, -) - from django.db import models from addon_service.authorized_account.link.models import AuthorizedLinkAccount @@ -11,20 +6,7 @@ validate_link_imp_number, ) from addon_service.external_service.models import ExternalService - - -class SupportedResourceTypes(Flag): - ADD_UPDATE_FILES = auto() - ADD_UPDATE_FILES_PARTIAL = auto() - DELETE_FILES = auto() - DELETE_FILES_PARTIAL = auto() - FORKING = auto() - LOGS = auto() - PERMISSIONS = auto() - REGISTERING = auto() - FILE_VERSIONS = auto() - COPY_INTO = auto() - DOWNLOAD_AS_ZIP = auto() +from addon_toolkit.interfaces.link import SupportedResourceTypes def validate_supported_features(value): diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py index 85c5469a..682057de 100644 --- a/addon_toolkit/interfaces/link.py +++ b/addon_toolkit/interfaces/link.py @@ -1,7 +1,5 @@ """a static (and still in progress) definition of what composes a link addon""" -from __future__ import annotations - import dataclasses import enum import typing @@ -17,9 +15,6 @@ from ._base import BaseAddonInterface -if typing.TYPE_CHECKING: - from addon_service.external_service.link.models import SupportedResourceTypes - __all__ = ( "ItemResult", "ItemType", @@ -34,6 +29,20 @@ class ItemType(enum.StrEnum): FOLDER = enum.auto() +class SupportedResourceTypes(enum.Flag): + ADD_UPDATE_FILES = enum.auto() + ADD_UPDATE_FILES_PARTIAL = enum.auto() + DELETE_FILES = enum.auto() + DELETE_FILES_PARTIAL = enum.auto() + FORKING = enum.auto() + LOGS = enum.auto() + PERMISSIONS = enum.auto() + REGISTERING = enum.auto() + FILE_VERSIONS = enum.auto() + COPY_INTO = enum.auto() + DOWNLOAD_AS_ZIP = enum.auto() + + @dataclasses.dataclass class ItemResult: item_id: str From c44132336c26e7e1fe4a8e130756f09aeb91c810 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 31 Mar 2025 13:28:35 +0300 Subject: [PATCH 006/100] added proper resource types --- addon_toolkit/interfaces/link.py | 43 ++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py index 682057de..14b12e89 100644 --- a/addon_toolkit/interfaces/link.py +++ b/addon_toolkit/interfaces/link.py @@ -30,17 +30,38 @@ class ItemType(enum.StrEnum): class SupportedResourceTypes(enum.Flag): - ADD_UPDATE_FILES = enum.auto() - ADD_UPDATE_FILES_PARTIAL = enum.auto() - DELETE_FILES = enum.auto() - DELETE_FILES_PARTIAL = enum.auto() - FORKING = enum.auto() - LOGS = enum.auto() - PERMISSIONS = enum.auto() - REGISTERING = enum.auto() - FILE_VERSIONS = enum.auto() - COPY_INTO = enum.auto() - DOWNLOAD_AS_ZIP = enum.auto() + AUDIOVISUAL = enum.auto() + AWARD = enum.auto() + BOOK = enum.auto() + BOOK_CHAPTER = enum.auto() + COLLECTION = enum.auto() + COMPUTATIONAL_NOTEBOOK = enum.auto() + CONFERENCE_PAPER = enum.auto() + CONFERENCE_PROCEEDING = enum.auto() + DATA_PAPER = enum.auto() + DATASET = enum.auto() + DISSERTATION = enum.auto() + EVENT = enum.auto() + IMAGE = enum.auto() + INSTRUMENT = enum.auto() + INTERACTIVE_RESOURCE = enum.auto() + JOURNAL = enum.auto() + JOURNAL_ARTICLE = enum.auto() + MODEL = enum.auto() + OUTPUT_MANAGEMENT_PLAN = enum.auto() + PEER_REVIEW = enum.auto() + PHYSICAL_OBJECT = enum.auto() + PREPRINT = enum.auto() + PROJECT = enum.auto() + REPORT = enum.auto() + SERVICE = enum.auto() + SOFTWARE = enum.auto() + SOUND = enum.auto() + STANDARD = enum.auto() + STUDY_REGISTRATION = enum.auto() + TEXT = enum.auto() + WORKFLOW = enum.auto() + OTHER = enum.auto() @dataclasses.dataclass From 434e1506005b3b3d94e1833cfaf9197269b6170c Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 31 Mar 2025 13:37:23 +0300 Subject: [PATCH 007/100] added validator to resource type --- addon_service/configured_addon/link/models.py | 11 ++++++++++- addon_service/configured_addon/link/serializers.py | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index e42ae069..8282dd4a 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -1,13 +1,22 @@ +from django.core.exceptions import ValidationError from django.db import models from addon_service.configured_addon.models import ConfiguredAddon +from addon_toolkit.interfaces.link import SupportedResourceTypes + + +def is_supported_resource_type(resource_type: int): + try: + SupportedResourceTypes(resource_type) + except ValueError: + raise ValidationError("Invalid resource type") class ConfiguredLinkAddon(ConfiguredAddon): target_uri = models.URLField() target_id = models.CharField() - int_resource_type = models.IntegerField() + int_resource_type = models.IntegerField(validators=[is_supported_resource_type]) class Meta: verbose_name = "Configured Link Addon" diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index db55a035..77369f3b 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -52,7 +52,7 @@ class ConfiguredLinkAddonSerializer(ConfiguredAddonSerializer): ) included_serializers = { - "base_account": ("addon_service.serializers.AuthorizedLinkAccountSerializer"), + "base_account": "addon_service.serializers.AuthorizedLinkAccountSerializer", "external_link_service": "addon_service.serializers.ExternalLinkServiceSerializer", "authorized_resource": "addon_service.serializers.ResourceReferenceSerializer", "connected_operations": "addon_service.serializers.AddonOperationSerializer", From 30ea16aa5805703da6c76756d984ea3bd44d095f Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 1 Apr 2025 17:02:10 +0300 Subject: [PATCH 008/100] fixed some things whcih vere left over --- addon_service/addon_operation_invocation/views.py | 10 ++++++++++ .../authorized_account/polymorphic_serializers.py | 4 ++++ addon_service/configured_addon/link/models.py | 8 +++++++- addon_service/configured_addon/link/serializers.py | 8 ++++++-- .../configured_addon/polymorphic_serializers.py | 4 ++++ 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/addon_service/addon_operation_invocation/views.py b/addon_service/addon_operation_invocation/views.py index da7736d3..36d29b2d 100644 --- a/addon_service/addon_operation_invocation/views.py +++ b/addon_service/addon_operation_invocation/views.py @@ -19,10 +19,12 @@ from ..authorized_account.computing.serializers import ( AuthorizedComputingAccountSerializer, ) +from ..authorized_account.link.serializers import AuthorizedLinkAccountSerializer from ..authorized_account.models import AuthorizedAccount from ..authorized_account.storage.serializers import AuthorizedStorageAccountSerializer from ..configured_addon.citation.serializers import ConfiguredCitationAddonSerializer from ..configured_addon.computing.serializers import ConfiguredComputingAddonSerializer +from ..configured_addon.link.serializers import ConfiguredLinkAddonSerializer from ..configured_addon.models import ConfiguredAddon from ..configured_addon.storage.serializers import ConfiguredStorageAddonSerializer from .models import AddonOperationInvocation @@ -63,6 +65,10 @@ def retrieve_related(self, request, *args, **kwargs): serializer = AuthorizedComputingAccountSerializer( instance, context={"request": request} ) + elif hasattr(instance, "authorizedlinkaccount"): + serializer = AuthorizedLinkAccountSerializer( + instance, context={"request": request} + ) else: raise ValueError("unknown authorized account type") elif isinstance(instance, ConfiguredAddon): @@ -78,6 +84,10 @@ def retrieve_related(self, request, *args, **kwargs): serializer = ConfiguredComputingAddonSerializer( instance, context={"request": request} ) + elif hasattr(instance, "configuredcomputingaddon"): + serializer = ConfiguredLinkAddonSerializer( + instance, context={"request": request} + ) else: raise ValueError("unknown configured addon type") else: diff --git a/addon_service/authorized_account/polymorphic_serializers.py b/addon_service/authorized_account/polymorphic_serializers.py index 773c63d0..9c3eea90 100644 --- a/addon_service/authorized_account/polymorphic_serializers.py +++ b/addon_service/authorized_account/polymorphic_serializers.py @@ -6,6 +6,9 @@ from addon_service.authorized_account.computing.serializers import ( AuthorizedComputingAccountSerializer, ) +from addon_service.authorized_account.link.serializers import ( + AuthorizedLinkAccountSerializer, +) from addon_service.authorized_account.models import AuthorizedAccount from addon_service.authorized_account.storage.serializers import ( AuthorizedStorageAccountSerializer, @@ -17,6 +20,7 @@ class AuthorizedAccountPolymorphicSerializer(serializers.PolymorphicModelSeriali AuthorizedCitationAccountSerializer, AuthorizedComputingAccountSerializer, AuthorizedStorageAccountSerializer, + AuthorizedLinkAccountSerializer, ] class Meta: diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index 8282dd4a..b97426f7 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -7,7 +7,9 @@ def is_supported_resource_type(resource_type: int): try: - SupportedResourceTypes(resource_type) + resource_type = SupportedResourceTypes(resource_type) + if len(resource_type) != 1: + raise ValidationError("One may select only one resource type") except ValueError: raise ValidationError("Invalid resource type") @@ -18,6 +20,10 @@ class ConfiguredLinkAddon(ConfiguredAddon): target_id = models.CharField() int_resource_type = models.IntegerField(validators=[is_supported_resource_type]) + @property + def resource_type(self) -> str: + return SupportedResourceTypes(self.int_resource_type).name + class Meta: verbose_name = "Configured Link Addon" verbose_name_plural = "Configured Link Addons" diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index 77369f3b..f339f7c1 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -1,6 +1,5 @@ from rest_framework.fields import ( CharField, - IntegerField, URLField, ) from rest_framework_json_api.relations import ResourceRelatedField @@ -8,6 +7,7 @@ from addon_service.addon_operation.models import AddonOperationModel from addon_service.common import view_names +from addon_service.common.enum_serializers import EnumNameChoiceField from addon_service.common.serializer_fields import DataclassRelatedLinkField from addon_service.configured_addon.serializers import ConfiguredAddonSerializer from addon_service.external_service.link.models import ExternalLinkService @@ -15,6 +15,7 @@ AuthorizedLinkAccount, ConfiguredLinkAddon, ) +from addon_toolkit.interfaces.link import SupportedResourceTypes RESOURCE_TYPE = get_resource_type_from_model(ConfiguredLinkAddon) @@ -25,7 +26,7 @@ class ConfiguredLinkAddonSerializer(ConfiguredAddonSerializer): target_uri = URLField() target_id = CharField() - int_resource_type = IntegerField() + resource_type = EnumNameChoiceField(enum_cls=SupportedResourceTypes) connected_operations = DataclassRelatedLinkField( dataclass_model=AddonOperationModel, @@ -75,4 +76,7 @@ class Meta: "external_link_service", "current_user_is_owner", "external_service_name", + "resource_type", + "target_uri", + "target_id", ] diff --git a/addon_service/configured_addon/polymorphic_serializers.py b/addon_service/configured_addon/polymorphic_serializers.py index 28b58d13..26fb44cb 100644 --- a/addon_service/configured_addon/polymorphic_serializers.py +++ b/addon_service/configured_addon/polymorphic_serializers.py @@ -6,6 +6,9 @@ from addon_service.configured_addon.computing.serializers import ( ConfiguredComputingAddonSerializer, ) +from addon_service.configured_addon.link.serializers import ( + ConfiguredLinkAddonSerializer, +) from addon_service.configured_addon.models import ConfiguredAddon from addon_service.configured_addon.storage.serializers import ( ConfiguredStorageAddonSerializer, @@ -17,6 +20,7 @@ class ConfiguredAddonPolymorphicSerializer(serializers.PolymorphicModelSerialize ConfiguredCitationAddonSerializer, ConfiguredComputingAddonSerializer, ConfiguredStorageAddonSerializer, + ConfiguredLinkAddonSerializer, ] class Meta: From 47c34ed23649efc61e07a0d846f8e031043a0346 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak <158075011+opaduchak@users.noreply.github.com> Date: Wed, 2 Apr 2025 18:46:54 +0300 Subject: [PATCH 009/100] Apply suggestions from code review Co-authored-by: Yuhuai Liu --- addon_service/addon_operation_invocation/views.py | 2 +- addon_service/authorized_account/link/serializers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addon_service/addon_operation_invocation/views.py b/addon_service/addon_operation_invocation/views.py index 36d29b2d..579c44f7 100644 --- a/addon_service/addon_operation_invocation/views.py +++ b/addon_service/addon_operation_invocation/views.py @@ -84,7 +84,7 @@ def retrieve_related(self, request, *args, **kwargs): serializer = ConfiguredComputingAddonSerializer( instance, context={"request": request} ) - elif hasattr(instance, "configuredcomputingaddon"): + elif hasattr(instance, "configuredlinkaddon"): serializer = ConfiguredLinkAddonSerializer( instance, context={"request": request} ) diff --git a/addon_service/authorized_account/link/serializers.py b/addon_service/authorized_account/link/serializers.py index 0d8fbc6c..68c22d6e 100644 --- a/addon_service/authorized_account/link/serializers.py +++ b/addon_service/authorized_account/link/serializers.py @@ -20,7 +20,7 @@ ) -RESOURCE_TYPE = get_resource_type_from_model(AuthorizedStorageAccount) +RESOURCE_TYPE = get_resource_type_from_model(AuthorizedLinkAccount) class AuthorizedLinkAccountSerializer(AuthorizedAccountSerializer): From d625575224139d7cfafbe65da5a7d499d2921fee Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 4 Apr 2025 14:19:06 +0300 Subject: [PATCH 010/100] more complete implementation of link structures --- addon_imps/link/__init__.py | 0 addon_imps/link/dataverse.py | 195 ++++++++++++++++++ addon_service/addon_imp/instantiation.py | 33 +++ addon_service/admin/__init__.py | 4 +- .../authorized_account/link/serializers.py | 7 +- .../authorized_account/link/views.py | 2 +- addon_service/authorized_account/utils.py | 4 +- addon_service/common/enum_serializers.py | 2 + addon_service/common/known_imps.py | 39 ++-- addon_service/configured_addon/link/models.py | 15 +- .../configured_addon/link/serializers.py | 6 +- addon_service/configured_addon/serializers.py | 4 +- addon_service/credentials/serializers.py | 4 + addon_service/external_service/link/models.py | 6 +- ...er_externallinkservice_options_and_more.py | 49 +++++ addon_service/resource_reference/models.py | 11 + .../resource_reference/serializers.py | 9 + addon_service/serializers.py | 12 ++ addon_service/urls.py | 3 + addon_service/user_reference/models.py | 7 + addon_service/user_reference/serializers.py | 10 + addon_service/views.py | 6 + addon_toolkit/interfaces/__init__.py | 2 + addon_toolkit/interfaces/link.py | 11 +- 24 files changed, 399 insertions(+), 42 deletions(-) create mode 100644 addon_imps/link/__init__.py create mode 100644 addon_imps/link/dataverse.py create mode 100644 addon_service/migrations/0013_alter_externallinkservice_options_and_more.py diff --git a/addon_imps/link/__init__.py b/addon_imps/link/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/addon_imps/link/dataverse.py b/addon_imps/link/dataverse.py new file mode 100644 index 00000000..7a98e9af --- /dev/null +++ b/addon_imps/link/dataverse.py @@ -0,0 +1,195 @@ +from __future__ import annotations + +import asyncio +import re +from dataclasses import dataclass + +from django.core.exceptions import ValidationError + +from addon_toolkit.interfaces.link import ( + ItemResult, + ItemSampleResult, + ItemType, + LinkAddonHttpRequestorImp, +) + + +DATAVERSE_REGEX = re.compile(r"^dataverse/(?P\d*)$") +DATASET_REGEX = re.compile(r"^dataset/(?P.*)$") + + +@dataclass +class DataverseLinkImp(LinkAddonHttpRequestorImp): + """storage on dataverse + + see https://guides.dataverse.org/en/latest/api/native-api.html + """ + + async def build_url_for_id(self, item_id: str) -> str: + dataset_id = DATASET_REGEX.match(item_id)["id"] + dataset = await self._fetch_dataset(dataset_id) + return dataset.item_id + + async def get_external_account_id(self, _: dict[str, str]) -> str: + try: + async with self.network.GET("api/v1/users/:me") as response: + if not response.http_status.is_success: + raise ValidationError( + "Could not get dataverse account id, check your API Token" + ) + content = await response.json_content() + return content.get("data", {}).get("id") + except ValueError as exc: + if "relative url may not alter the base url" in str(exc).lower(): + raise ValidationError( + "Invalid host URL. Please check your Dataverse base URL." + ) + raise + + async def list_root_items(self, page_cursor: str = "") -> ItemSampleResult: + async with self.network.GET( + "api/mydata/retrieve", + query=[ + ["selected_page", page_cursor], + *[("role_ids", role) for role in range(1, 9)], + ("dvobject_types", "Dataverse"), + *[ + ("published_states", state) + for state in [ + "Unpublished", + "Published", + "Draft", + "Deaccessioned", + "In+Review", + ] + ], + ], + ) as response: + content = await response.json_content() + if resp_data := content.get("data"): + return parse_mydata(resp_data) + return ItemSampleResult(items=[], total_count=0) + + async def get_item_info(self, item_id: str) -> ItemResult: + if not item_id: + return ItemResult(item_id="", item_name="", item_type=ItemType.FOLDER) + elif match := DATAVERSE_REGEX.match(item_id): + entity = await self._fetch_dataverse(match["id"]) + elif match := DATASET_REGEX.match(item_id): + entity = await self._fetch_dataset(persistent_id=match["id"]) + else: + raise ValueError(f"Invalid item id: {item_id}") + + return entity + + async def list_child_items( + self, + item_id: str, + page_cursor: str = "", + item_type: ItemType | None = None, + ) -> ItemSampleResult: + if not item_id: + return await self.list_root_items(page_cursor) + elif match := DATAVERSE_REGEX.match(item_id): + items = await self._fetch_dataverse_items(match["id"]) + return ItemSampleResult( + items=items, + total_count=len(items), + ) + else: + return ItemSampleResult(items=[], total_count=0) + + async def _fetch_dataverse_items(self, dataverse_id) -> list[ItemResult]: + async with self.network.GET( + f"api/dataverses/{dataverse_id}/contents" + ) as response: + response_content = await response.json_content() + return await asyncio.gather( + *[ + self.get_dataverse_or_dataset_item(item) + for item in response_content["data"] + ] + ) + + async def get_dataverse_or_dataset_item(self, item: dict): + match item["type"]: + case "dataset": + return await self._fetch_dataset(dataset_id=item["id"]) + case "dataverse": + return parse_dataverse_as_subitem(item) + raise ValueError(f"Invalid item type: {item['type']}") + + async def _fetch_dataverse(self, dataverse_id) -> ItemResult: + async with self.network.GET(f"api/dataverses/{dataverse_id}") as response: + return parse_dataverse(await response.json_content()) + + async def _fetch_dataset( + self, dataset_id: str = None, persistent_id: str = None + ) -> ItemResult: + url = f"api/datasets/{':persistentId' if persistent_id else dataset_id}" + query = {"persistentId": persistent_id} if persistent_id else {} + # async with self.network.GET("api/datasets/:persistentId/", query={'persistentId': dataset_id}) as response: + async with self.network.GET(url, query=query) as response: + return parse_dataset(await response.json_content()) + + +### +# module-local helpers + + +def parse_dataverse_as_subitem(data: dict): + return ItemResult( + item_type=ItemType.FOLDER, + item_name=data["title"], + item_id=f'dataverse/{data["id"]}', + ) + + +def parse_dataverse(data: dict): + if data.get("data"): + data = data["data"] + return ItemResult( + item_type=ItemType.FOLDER, + item_name=data["name"], + item_id=f'dataverse/{data["id"]}', + ) + + +def parse_mydata(data: dict): + if data.get("data"): + data = data["data"] + return ItemSampleResult( + items=[ + ItemResult( + item_id=f"dataverse/{file['entity_id']}", + item_name=file["name"], + item_type=ItemType.FOLDER, + ) + for file in data["items"] + ], + total_count=data["total_count"], + next_sample_cursor=( + data["pagination"]["nextPageNumber"] + if data["pagination"]["hasNextPageNumber"] + else None + ), + ) + + +def parse_dataset(data: dict) -> ItemResult: + if data.get("data"): + data = data["data"] + try: + return ItemResult( + item_id=f'dataset/{data['latestVersion']["datasetPersistentId"]}', + item_name=[ + item + for item in data["latestVersion"]["metadataBlocks"]["citation"][ + "fields" + ] + if item["typeName"] == "title" + ][0]["value"], + item_type=ItemType.FOLDER, + ) + except (KeyError, IndexError) as e: + raise ValueError(f"Invalid dataset response: {e=}") diff --git a/addon_service/addon_imp/instantiation.py b/addon_service/addon_imp/instantiation.py index 427220cc..9466a031 100644 --- a/addon_service/addon_imp/instantiation.py +++ b/addon_service/addon_imp/instantiation.py @@ -17,6 +17,11 @@ ComputingAddonImp, ComputingConfig, ) +from addon_toolkit.interfaces.link import ( + LinkAddonClientRequestorImp, + LinkAddonHttpRequestorImp, + LinkAddonImp, +) from addon_toolkit.interfaces.storage import ( StorageAddonClientRequestorImp, StorageAddonHttpRequestorImp, @@ -26,6 +31,7 @@ if TYPE_CHECKING: + from addon_service.authorized_account.link.models import AuthorizedLinkAccount from addon_service.authorized_account.models import AuthorizedAccount from addon_service.models import ( AuthorizedCitationAccount, @@ -45,6 +51,8 @@ async def get_addon_instance( return await get_citation_addon_instance(imp_cls, account, config) elif issubclass(imp_cls, ComputingAddonImp): return await get_computing_addon_instance(imp_cls, account, config) + elif issubclass(imp_cls, LinkAddonImp): + return await get_link_addon_instance(imp_cls, account) raise ValueError(f"unknown addon type {imp_cls}") @@ -134,3 +142,28 @@ async def get_computing_addon_instance( get_computing_addon_instance__blocking = async_to_sync(get_computing_addon_instance) + + +async def get_link_addon_instance( + imp_cls: type[LinkAddonImp], + account: AuthorizedLinkAccount, +) -> LinkAddonImp: + """create an instance of a `linkAddonImp`""" + + assert issubclass(imp_cls, AddonImp) + assert imp_cls is not LinkAddonImp, "Addons shouldn't directly extend LinkAddonImp" + if issubclass(imp_cls, LinkAddonHttpRequestorImp): + imp = imp_cls( + network=GravyvaletHttpRequestor( + client_session=await get_singleton_client_session(), + prefix_url=account.external_service.api_base_url, + account=account, + ), + ) + if issubclass(imp_cls, LinkAddonClientRequestorImp): + imp = imp_cls(credentials=await account.get_credentials__async()) + + return imp + + +get_link_addon_instance__blocking = async_to_sync(get_link_addon_instance) diff --git a/addon_service/admin/__init__.py b/addon_service/admin/__init__.py index e06f11b1..59347d5c 100644 --- a/addon_service/admin/__init__.py +++ b/addon_service/admin/__init__.py @@ -61,7 +61,7 @@ class ExternalLinkServiceAdmin(GravyvaletModelAdmin): ) raw_id_fields = ("oauth2_client_config", "oauth1_client_config") enum_choice_fields = { - "int_addon_imp": known_imps.AddonImpNumbers, + "int_addon_imp": known_imps.LinkAddonImpNumbers, "int_credentials_format": CredentialsFormats, "int_service_type": ServiceTypes, } @@ -80,7 +80,7 @@ class ExternalComputingServiceAdmin(GravyvaletModelAdmin): ) raw_id_fields = ("oauth2_client_config",) enum_choice_fields = { - "int_addon_imp": known_imps.AddonImpNumbers, + "int_addon_imp": known_imps.ComputingAddonImpNumbers, "int_credentials_format": CredentialsFormats, "int_service_type": ServiceTypes, } diff --git a/addon_service/authorized_account/link/serializers.py b/addon_service/authorized_account/link/serializers.py index 68c22d6e..8de8350f 100644 --- a/addon_service/authorized_account/link/serializers.py +++ b/addon_service/authorized_account/link/serializers.py @@ -6,6 +6,7 @@ from rest_framework_json_api.utils import get_resource_type_from_model from addon_service.addon_operation.models import AddonOperationModel +from addon_service.authorized_account.link.models import AuthorizedLinkAccount from addon_service.authorized_account.serializers import AuthorizedAccountSerializer from addon_service.common import view_names from addon_service.common.serializer_fields import ( @@ -13,7 +14,6 @@ ReadOnlyResourceRelatedField, ) from addon_service.models import ( - AuthorizedStorageAccount, ConfiguredLinkAddon, ExternalLinkService, UserReference, @@ -53,7 +53,7 @@ class AuthorizedLinkAccountSerializer(AuthorizedAccountSerializer): included_serializers = { "account_owner": "addon_service.serializers.UserReferenceSerializer", "external_link_service": "addon_service.serializers.ExternalLinkServiceSerializer", - "configured_link_addons": "addon_service.serializers.ConfiguredLinkSerializer", + "configured_link_addons": "addon_service.serializers.ConfiguredLinkAddonSerializer", "authorized_operations": "addon_service.serializers.AddonOperationSerializer", } @@ -65,7 +65,7 @@ def get_configured_addons_uris(self, obj): ) class Meta: - model = AuthorizedStorageAccount + model = AuthorizedLinkAccount fields = [ "id", "url", @@ -78,7 +78,6 @@ class Meta: "authorized_operation_names", "configured_link_addons", "credentials", - "default_root_folder", "external_link_service", "initiate_oauth", "credentials_available", diff --git a/addon_service/authorized_account/link/views.py b/addon_service/authorized_account/link/views.py index c8d81e58..c6ea9ab6 100644 --- a/addon_service/authorized_account/link/views.py +++ b/addon_service/authorized_account/link/views.py @@ -4,6 +4,6 @@ from .serializers import AuthorizedLinkAccountSerializer -class AuthorizedStorageAccountViewSet(AuthorizedAccountViewSet): +class AuthorizedLinkAccountViewSet(AuthorizedAccountViewSet): queryset = AuthorizedLinkAccount.objects.all() serializer_class = AuthorizedLinkAccountSerializer diff --git a/addon_service/authorized_account/utils.py b/addon_service/authorized_account/utils.py index 5dfd4c60..2f691dd9 100644 --- a/addon_service/authorized_account/utils.py +++ b/addon_service/authorized_account/utils.py @@ -4,6 +4,7 @@ from addon_toolkit.interfaces.citation import CitationAddonImp from addon_toolkit.interfaces.computing import ComputingAddonImp +from addon_toolkit.interfaces.link import LinkAddonImp from addon_toolkit.interfaces.storage import StorageAddonImp @@ -18,5 +19,6 @@ def get_config_for_account(account: AuthorizedAccount): return account.authorizedcitationaccount.config elif issubclass(account.imp_cls, ComputingAddonImp): return account.authorizedcomputingaccount.config - + elif issubclass(account.imp_cls, LinkAddonImp): + return None raise ValueError(f"this function implementation does not support {account.imp_cls}") diff --git a/addon_service/common/enum_serializers.py b/addon_service/common/enum_serializers.py index dcc929a3..6ea1453a 100644 --- a/addon_service/common/enum_serializers.py +++ b/addon_service/common/enum_serializers.py @@ -41,6 +41,8 @@ def to_internal_value(self, data) -> enum.Enum: return self.enum_cls[_name] def to_representation(self, value: enum.Enum): + if isinstance(value, str): + value = self.enum_cls[value] return super().to_representation(value).name diff --git a/addon_service/common/known_imps.py b/addon_service/common/known_imps.py index 7557f953..e6b49653 100644 --- a/addon_service/common/known_imps.py +++ b/addon_service/common/known_imps.py @@ -10,6 +10,7 @@ zotero_org, ) from addon_imps.computing import boa +from addon_imps.link import dataverse as link_dataverse from addon_imps.storage import ( bitbucket, box_dot_com, @@ -25,6 +26,10 @@ ) from addon_service.common.enum_decorators import enum_names_same_as from addon_toolkit import AddonImp +from addon_toolkit.interfaces.citation import CitationAddonImp +from addon_toolkit.interfaces.computing import ComputingAddonImp +from addon_toolkit.interfaces.link import LinkAddonImp +from addon_toolkit.interfaces.storage import StorageAddonImp if __debug__: @@ -79,7 +84,7 @@ class KnownAddonImps(enum.Enum): BITBUCKET = bitbucket.BitbucketStorageImp DATAVERSE = dataverse.DataverseStorageImp OWNCLOUD = owncloud.OwnCloudStorageImp - + LINK_DATAVERSE = link_dataverse.DataverseLinkImp GITHUB = github.GitHubStorageImp GITLAB = gitlab.GitlabStorageImp DROPBOX = dropbox.DropboxStorageImp @@ -108,6 +113,7 @@ class AddonImpNumbers(enum.Enum): GITLAB = 1011 BITBUCKET = 1012 + LINK_DATAVERSE = 1030 GITHUB = 1013 BOA = 1020 @@ -116,20 +122,17 @@ class AddonImpNumbers(enum.Enum): BLARG = -7 -StorageAddonImpNumbers = frozenset( - { - AddonImpNumbers.BOX, - AddonImpNumbers.S3, - AddonImpNumbers.GOOGLEDRIVE, - AddonImpNumbers.DROPBOX, - AddonImpNumbers.FIGSHARE, - AddonImpNumbers.ONEDRIVE, - AddonImpNumbers.OWNCLOUD, - AddonImpNumbers.DATAVERSE, - AddonImpNumbers.GITLAB, - AddonImpNumbers.BITBUCKET, - AddonImpNumbers.GITHUB, - } -) -CitationAddonImpNumbers = frozenset({AddonImpNumbers.ZOTERO, AddonImpNumbers.MENDELEY}) -ComputingAddonImpNumbers = frozenset({AddonImpNumbers.BOA}) +def filter_addons_by_type(addon_type): + return frozenset( + { + AddonImpNumbers[item.name] + for item in KnownAddonImps + if issubclass(item.value, addon_type) + } + ) + + +StorageAddonImpNumbers = filter_addons_by_type(StorageAddonImp) +CitationAddonImpNumbers = filter_addons_by_type(CitationAddonImp) +ComputingAddonImpNumbers = filter_addons_by_type(ComputingAddonImp) +LinkAddonImpNumbers = filter_addons_by_type(LinkAddonImp) diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index b97426f7..13af2118 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -1,3 +1,4 @@ +from asgiref.sync import async_to_sync from django.core.exceptions import ValidationError from django.db import models @@ -16,7 +17,6 @@ def is_supported_resource_type(resource_type: int): class ConfiguredLinkAddon(ConfiguredAddon): - target_uri = models.URLField() target_id = models.CharField() int_resource_type = models.IntegerField(validators=[is_supported_resource_type]) @@ -24,6 +24,19 @@ class ConfiguredLinkAddon(ConfiguredAddon): def resource_type(self) -> str: return SupportedResourceTypes(self.int_resource_type).name + @resource_type.setter + def resource_type(self, value) -> None: + self.int_resource_type = value.value + + def target_url(self): + if self.target_id: + from addon_service.addon_imp.instantiation import ( + get_link_addon_instance__blocking, + ) + + addon = get_link_addon_instance__blocking(self.imp_cls, self.base_account) + return async_to_sync(addon.build_url_for_id)(self.target_id) + class Meta: verbose_name = "Configured Link Addon" verbose_name_plural = "Configured Link Addons" diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index f339f7c1..fea9efc3 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -24,7 +24,7 @@ class ConfiguredLinkAddonSerializer(ConfiguredAddonSerializer): """api serializer for the `ConfiguredLinkAddon` model""" - target_uri = URLField() + target_url = URLField(read_only=True) target_id = CharField() resource_type = EnumNameChoiceField(enum_cls=SupportedResourceTypes) @@ -64,9 +64,8 @@ class Meta: read_only_fields = ["external_link_service"] fields = [ "id", - "url", "display_name", - "root_folder", + "target_url", "base_account", "authorized_resource", "authorized_resource_uri", @@ -77,6 +76,5 @@ class Meta: "current_user_is_owner", "external_service_name", "resource_type", - "target_uri", "target_id", ] diff --git a/addon_service/configured_addon/serializers.py b/addon_service/configured_addon/serializers.py index 05c9e865..ce6d92ad 100644 --- a/addon_service/configured_addon/serializers.py +++ b/addon_service/configured_addon/serializers.py @@ -6,7 +6,7 @@ from addon_toolkit import AddonCapabilities -REQUIRED_FIELDS = frozenset(["url", "connected_operations", "authorized_resource"]) +REQUIRED_FIELDS = frozenset(["connected_operations", "authorized_resource"]) class ConfiguredAddonSerializer(serializers.HyperlinkedModelSerializer): @@ -14,7 +14,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if not REQUIRED_FIELDS.issubset(set(self.fields.keys())): raise Exception( - f"{self.__class__.__name__} requires {self.REQUIRED_FIELDS} to be instantiated" + f"{self.__class__.__name__} requires {REQUIRED_FIELDS} to be instantiated" ) connected_capabilities = EnumNameMultipleChoiceField(enum_cls=AddonCapabilities) diff --git a/addon_service/credentials/serializers.py b/addon_service/credentials/serializers.py index 23b4ece1..cb5e8863 100644 --- a/addon_service/credentials/serializers.py +++ b/addon_service/credentials/serializers.py @@ -1,3 +1,5 @@ +import json + from rest_framework.serializers import ( JSONField, ValidationError, @@ -21,6 +23,8 @@ def __init__(self, write_only=True, required=False, *args, **kwargs): def to_internal_value(self, data): # this issue still hasn't been fixed on FE, so keeping this for now + if not isinstance(data, dict): + data = json.loads(data) if not data or not any(data.values()): return None # consider empty {} same as omitting the field # No access to the credentials format here, so just try all of them diff --git a/addon_service/external_service/link/models.py b/addon_service/external_service/link/models.py index 41c819ad..dd2bfa92 100644 --- a/addon_service/external_service/link/models.py +++ b/addon_service/external_service/link/models.py @@ -14,21 +14,21 @@ def validate_supported_features(value): class ExternalLinkService(ExternalService): - int_supported_resource_types = models.IntegerField( + int_supported_resource_types = models.BigIntegerField( validators=[validate_supported_features], null=True ) @property def supported_resource_types(self) -> list[SupportedResourceTypes]: """get the enum representation of int_supported_features""" - return SupportedResourceTypes(self.int_supported_features) + return SupportedResourceTypes(self.int_supported_resource_types) @supported_resource_types.setter def supported_resource_types( self, new_supported_resource_types: SupportedResourceTypes ): """set int_authorized_capabilities without caring its int""" - self.int_supported_features = new_supported_resource_types.value + self.int_supported_resource_types = new_supported_resource_types.value def clean(self): super().clean() diff --git a/addon_service/migrations/0013_alter_externallinkservice_options_and_more.py b/addon_service/migrations/0013_alter_externallinkservice_options_and_more.py new file mode 100644 index 00000000..4ae8041a --- /dev/null +++ b/addon_service/migrations/0013_alter_externallinkservice_options_and_more.py @@ -0,0 +1,49 @@ +# Generated by Django 4.2.7 on 2025-04-03 10:10 + +from django.db import ( + migrations, + models, +) + +import addon_service.configured_addon.link.models +import addon_service.external_service.link.models + + +class Migration(migrations.Migration): + + dependencies = [ + ("addon_service", "0012_authorizedlinkaccount_configuredlinkaddon_and_more"), + ] + + operations = [ + migrations.AlterModelOptions( + name="externallinkservice", + options={ + "verbose_name": "External Link Service", + "verbose_name_plural": "External Link Services", + }, + ), + migrations.RemoveField( + model_name="configuredlinkaddon", + name="target_uri", + ), + migrations.AlterField( + model_name="configuredlinkaddon", + name="int_resource_type", + field=models.IntegerField( + validators=[ + addon_service.configured_addon.link.models.is_supported_resource_type + ] + ), + ), + migrations.AlterField( + model_name="externallinkservice", + name="int_supported_resource_types", + field=models.BigIntegerField( + null=True, + validators=[ + addon_service.external_service.link.models.validate_supported_features + ], + ), + ), + ] diff --git a/addon_service/resource_reference/models.py b/addon_service/resource_reference/models.py index d9004be6..b83021f8 100644 --- a/addon_service/resource_reference/models.py +++ b/addon_service/resource_reference/models.py @@ -4,6 +4,7 @@ from addon_service.common.base_model import AddonsServiceBaseModel from addon_service.configured_addon.citation.models import ConfiguredCitationAddon from addon_service.configured_addon.computing.models import ConfiguredComputingAddon +from addon_service.configured_addon.link.models import ConfiguredLinkAddon from addon_service.configured_addon.storage.models import ConfiguredStorageAddon @@ -22,6 +23,16 @@ def configured_storage_addons(self): .order_by(Lower("_display_name")) ) + @property + def configured_link_addons(self): + return ConfiguredLinkAddon.objects.filter( + authorized_resource=self + ).select_related( + "base_account__external_service__externallinkservice", + "base_account__authorizedlinkaccount", + "base_account__account_owner", + ) + @property def configured_citation_addons(self): return ConfiguredCitationAddon.objects.filter( diff --git a/addon_service/resource_reference/serializers.py b/addon_service/resource_reference/serializers.py index dccf363a..eb1a79d3 100644 --- a/addon_service/resource_reference/serializers.py +++ b/addon_service/resource_reference/serializers.py @@ -30,6 +30,11 @@ class ResourceReferenceSerializer(serializers.HyperlinkedModelSerializer): queryset=ConfiguredCitationAddon.objects.active(), related_link_view_name=view_names.related_view(RESOURCE_TYPE), ) + configured_link_addons = HyperlinkedRelatedField( + many=True, + queryset=ConfiguredCitationAddon.objects.active(), + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + ) configured_computing_addons = HyperlinkedRelatedField( many=True, queryset=ConfiguredComputingAddon.objects.active(), @@ -43,6 +48,9 @@ class ResourceReferenceSerializer(serializers.HyperlinkedModelSerializer): "configured_citation_addons": ( "addon_service.serializers.ConfiguredCitationAddonSerializer" ), + "configured_link_addons": ( + "addon_service.serializers.ConfiguredComputingAddonSerializer" + ), "configured_computing_addons": ( "addon_service.serializers.ConfiguredComputingAddonSerializer" ), @@ -55,6 +63,7 @@ class Meta: "url", "resource_uri", "configured_storage_addons", + "configured_link_addons", "configured_citation_addons", "configured_computing_addons", ] diff --git a/addon_service/serializers.py b/addon_service/serializers.py index e3f15b46..6cebd0f7 100644 --- a/addon_service/serializers.py +++ b/addon_service/serializers.py @@ -11,6 +11,9 @@ from addon_service.authorized_account.computing.serializers import ( AuthorizedComputingAccountSerializer, ) +from addon_service.authorized_account.link.serializers import ( + AuthorizedLinkAccountSerializer, +) from addon_service.authorized_account.polymorphic_serializers import ( AuthorizedAccountPolymorphicSerializer, ) @@ -24,6 +27,9 @@ from addon_service.configured_addon.computing.serializers import ( ConfiguredComputingAddonSerializer, ) +from addon_service.configured_addon.link.serializers import ( + ConfiguredLinkAddonSerializer, +) from addon_service.configured_addon.polymorphic_serializers import ( ConfiguredAddonPolymorphicSerializer, ) @@ -37,6 +43,9 @@ from addon_service.external_service.computing.serializers import ( ExternalComputingServiceSerializer, ) +from addon_service.external_service.link.serializers import ( + ExternalLinkServiceSerializer, +) from addon_service.external_service.serializers import ExternalServiceSerializer from addon_service.external_service.storage.serializers import ( ExternalStorageServiceSerializer, @@ -67,4 +76,7 @@ "AuthorizedAccountSerializer", "AuthorizedAccountPolymorphicSerializer", "ConfiguredAddonPolymorphicSerializer", + "ConfiguredLinkAddonSerializer", + "AuthorizedLinkAccountSerializer", + "ExternalLinkServiceSerializer", ) diff --git a/addon_service/urls.py b/addon_service/urls.py index d6b404bf..ab89009d 100644 --- a/addon_service/urls.py +++ b/addon_service/urls.py @@ -57,6 +57,9 @@ def _register_viewset(viewset): _register_viewset(views.AuthorizedCitationAccountViewSet) _register_viewset(views.ConfiguredCitationAddonViewSet) _register_viewset(views.ExternalCitationServiceViewSet) +_register_viewset(views.AuthorizedLinkAccountViewSet) +_register_viewset(views.ConfiguredLinkAddonViewSet) +_register_viewset(views.ExternalLinkServiceViewSet) _register_viewset(views.AuthorizedComputingAccountViewSet) _register_viewset(views.ConfiguredComputingAddonViewSet) _register_viewset(views.ExternalComputingServiceViewSet) diff --git a/addon_service/user_reference/models.py b/addon_service/user_reference/models.py index e54ad38d..3b2b39c4 100644 --- a/addon_service/user_reference/models.py +++ b/addon_service/user_reference/models.py @@ -3,6 +3,7 @@ from addon_service.authorized_account.citation.models import AuthorizedCitationAccount from addon_service.authorized_account.computing.models import AuthorizedComputingAccount +from addon_service.authorized_account.link.models import AuthorizedLinkAccount from addon_service.authorized_account.storage.models import AuthorizedStorageAccount from addon_service.common.base_model import AddonsServiceBaseModel from addon_service.configured_addon.computing.models import ConfiguredComputingAddon @@ -55,6 +56,12 @@ def authorized_computing_accounts(self): account_owner=self ).select_related("external_service") + @property + def authorized_link_accounts(self): + return AuthorizedLinkAccount.objects.filter(account_owner=self).select_related( + "external_service" + ) + class Meta: verbose_name = "User Reference" verbose_name_plural = "User References" diff --git a/addon_service/user_reference/serializers.py b/addon_service/user_reference/serializers.py index b45204e5..70df89e8 100644 --- a/addon_service/user_reference/serializers.py +++ b/addon_service/user_reference/serializers.py @@ -34,6 +34,12 @@ class UserReferenceSerializer(serializers.HyperlinkedModelSerializer): related_link_view_name=view_names.related_view(RESOURCE_TYPE), ) + authorized_link_accounts = HyperlinkedRelatedField( + many=True, + queryset=AuthorizedCitationAccount.objects.all(), + related_link_view_name=view_names.related_view(RESOURCE_TYPE), + ) + authorized_computing_accounts = HyperlinkedRelatedField( many=True, queryset=AuthorizedComputingAccount.objects.all(), @@ -56,6 +62,9 @@ class UserReferenceSerializer(serializers.HyperlinkedModelSerializer): "authorized_computing_accounts": ( "addon_service.serializers.AuthorizedComputingAccountSerializer" ), + "authorized_link_accounts": ( + "addon_service.serializers.AuthorizedComputingAccountSerializer" + ), "configured_resources": ( "addon_service.serializers.ResourceReferenceSerializer" ), @@ -70,5 +79,6 @@ class Meta: "authorized_storage_accounts", "authorized_citation_accounts", "authorized_computing_accounts", + "authorized_link_accounts", "configured_resources", ] diff --git a/addon_service/views.py b/addon_service/views.py index 4f7078a8..f72ee948 100644 --- a/addon_service/views.py +++ b/addon_service/views.py @@ -16,6 +16,7 @@ from addon_service.authorized_account.computing.views import ( AuthorizedComputingAccountViewSet, ) +from addon_service.authorized_account.link.views import AuthorizedLinkAccountViewSet from addon_service.authorized_account.storage.views import ( AuthorizedStorageAccountViewSet, ) @@ -23,11 +24,13 @@ from addon_service.configured_addon.computing.views import ( ConfiguredComputingAddonViewSet, ) +from addon_service.configured_addon.link.views import ConfiguredLinkAddonViewSet from addon_service.configured_addon.storage.views import ConfiguredStorageAddonViewSet from addon_service.external_service.citation.views import ExternalCitationServiceViewSet from addon_service.external_service.computing.views import ( ExternalComputingServiceViewSet, ) +from addon_service.external_service.link.views import ExternalLinkServiceViewSet from addon_service.external_service.storage.views import ExternalStorageServiceViewSet from addon_service.oauth1.views import oauth1_callback_view from addon_service.oauth2.views import oauth2_callback_view @@ -61,6 +64,9 @@ async def status(request): "AuthorizedComputingAccountViewSet", "ConfiguredComputingAddonViewSet", "ExternalComputingServiceViewSet", + "AuthorizedLinkAccountViewSet", + "ConfiguredLinkAddonViewSet", + "ExternalLinkServiceViewSet", "AuthorizedStorageAccountViewSet", "ConfiguredStorageAddonViewSet", "ExternalStorageServiceViewSet", diff --git a/addon_toolkit/interfaces/__init__.py b/addon_toolkit/interfaces/__init__.py index 2b878dd7..e00b0158 100644 --- a/addon_toolkit/interfaces/__init__.py +++ b/addon_toolkit/interfaces/__init__.py @@ -3,6 +3,7 @@ from . import ( citation, computing, + link, storage, ) from ._base import BaseAddonInterface @@ -21,3 +22,4 @@ class AllAddonInterfaces(enum.Enum): STORAGE = storage.StorageAddonInterface CITATION = citation.CitationServiceInterface COMPUTING = computing.ComputingAddonInterface + LINK = link.LinkAddonInterface diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py index 14b12e89..82e203d2 100644 --- a/addon_toolkit/interfaces/link.py +++ b/addon_toolkit/interfaces/link.py @@ -25,7 +25,7 @@ class ItemType(enum.StrEnum): - FILE = enum.auto() + RESOURCE = enum.auto() FOLDER = enum.auto() @@ -69,7 +69,7 @@ class ItemResult: item_id: str item_name: str item_type: ItemType - resource_type: SupportedResourceTypes + resource_type: SupportedResourceTypes | None = None item_link: str | None = None @@ -100,6 +100,8 @@ def with_cursor(self, cursor: Cursor) -> typing.Self: class LinkAddonInterface(BaseAddonInterface, typing.Protocol): + async def build_url_for_id(self, item_id: str) -> str: ... + @immediate_operation(capability=AddonCapabilities.ACCESS) async def get_item_info(self, item_id: str) -> ItemResult: ... @@ -121,12 +123,9 @@ class LinkAddonImp(AddonImp): ADDON_INTERFACE = LinkAddonInterface - async def build_wb_config(self) -> dict: - return {} - @dataclasses.dataclass -class LinkAddonHttpRequestorImp(LinkAddonImp): +class LinkAddonHttpRequestorImp(LinkAddonImp, LinkAddonInterface): """base class for link addon implementations using GV network""" network: HttpRequestor From 9a32d59e1626b9fca5047d0e54c49b8195d1300e Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 4 Apr 2025 15:20:02 +0300 Subject: [PATCH 011/100] squashed migrations, fixed unit tests fixed resource type for configured link addon --- addon_service/configured_addon/link/models.py | 2 +- ...inkaccount_configuredlinkaddon_and_more.py | 19 ++++--- ...er_externallinkservice_options_and_more.py | 49 ------------------- .../test_by_type/test_resource_reference.py | 1 + .../tests/test_by_type/test_user_reference.py | 2 + 5 files changed, 17 insertions(+), 56 deletions(-) delete mode 100644 addon_service/migrations/0013_alter_externallinkservice_options_and_more.py diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index 13af2118..7dbfbe2a 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -18,7 +18,7 @@ def is_supported_resource_type(resource_type: int): class ConfiguredLinkAddon(ConfiguredAddon): target_id = models.CharField() - int_resource_type = models.IntegerField(validators=[is_supported_resource_type]) + int_resource_type = models.BigIntegerField(validators=[is_supported_resource_type]) @property def resource_type(self) -> str: diff --git a/addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py b/addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py index 707c5f06..87df7bb6 100644 --- a/addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py +++ b/addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.7 on 2025-03-28 12:06 +# Generated by Django 4.2.7 on 2025-04-04 12:12 import django.db.models.deletion from django.db import ( @@ -6,6 +6,7 @@ models, ) +import addon_service.configured_addon.link.models import addon_service.external_service.link.models @@ -51,9 +52,15 @@ class Migration(migrations.Migration): to="addon_service.configuredaddon", ), ), - ("target_uri", models.URLField()), ("target_id", models.CharField()), - ("int_resource_type", models.IntegerField()), + ( + "int_resource_type", + models.BigIntegerField( + validators=[ + addon_service.configured_addon.link.models.is_supported_resource_type + ] + ), + ), ], options={ "verbose_name": "Configured Link Addon", @@ -77,7 +84,7 @@ class Migration(migrations.Migration): ), ( "int_supported_resource_types", - models.IntegerField( + models.BigIntegerField( null=True, validators=[ addon_service.external_service.link.models.validate_supported_features @@ -86,8 +93,8 @@ class Migration(migrations.Migration): ), ], options={ - "verbose_name": "External Storage Service", - "verbose_name_plural": "External Storage Services", + "verbose_name": "External Link Service", + "verbose_name_plural": "External Link Services", }, bases=("addon_service.externalservice",), ), diff --git a/addon_service/migrations/0013_alter_externallinkservice_options_and_more.py b/addon_service/migrations/0013_alter_externallinkservice_options_and_more.py deleted file mode 100644 index 4ae8041a..00000000 --- a/addon_service/migrations/0013_alter_externallinkservice_options_and_more.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 4.2.7 on 2025-04-03 10:10 - -from django.db import ( - migrations, - models, -) - -import addon_service.configured_addon.link.models -import addon_service.external_service.link.models - - -class Migration(migrations.Migration): - - dependencies = [ - ("addon_service", "0012_authorizedlinkaccount_configuredlinkaddon_and_more"), - ] - - operations = [ - migrations.AlterModelOptions( - name="externallinkservice", - options={ - "verbose_name": "External Link Service", - "verbose_name_plural": "External Link Services", - }, - ), - migrations.RemoveField( - model_name="configuredlinkaddon", - name="target_uri", - ), - migrations.AlterField( - model_name="configuredlinkaddon", - name="int_resource_type", - field=models.IntegerField( - validators=[ - addon_service.configured_addon.link.models.is_supported_resource_type - ] - ), - ), - migrations.AlterField( - model_name="externallinkservice", - name="int_supported_resource_types", - field=models.BigIntegerField( - null=True, - validators=[ - addon_service.external_service.link.models.validate_supported_features - ], - ), - ), - ] diff --git a/addon_service/tests/test_by_type/test_resource_reference.py b/addon_service/tests/test_by_type/test_resource_reference.py index a9af0a4f..2a5f71e4 100644 --- a/addon_service/tests/test_by_type/test_resource_reference.py +++ b/addon_service/tests/test_by_type/test_resource_reference.py @@ -165,6 +165,7 @@ def test_get(self): "configured_storage_addons", "configured_citation_addons", "configured_computing_addons", + "configured_link_addons", }, ) diff --git a/addon_service/tests/test_by_type/test_user_reference.py b/addon_service/tests/test_by_type/test_user_reference.py index de0eeb56..9b3c79b9 100644 --- a/addon_service/tests/test_by_type/test_user_reference.py +++ b/addon_service/tests/test_by_type/test_user_reference.py @@ -67,6 +67,7 @@ def test_get(self): "authorized_storage_accounts", "authorized_citation_accounts", "authorized_computing_accounts", + "authorized_link_accounts", "configured_resources", }, ) @@ -202,6 +203,7 @@ def test_get(self): "authorized_storage_accounts", "authorized_citation_accounts", "authorized_computing_accounts", + "authorized_link_accounts", "configured_resources", }, ) From 2c1974f36e5091afd7971997482fe90d9d867a0b Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Fri, 7 Feb 2025 20:40:03 -0500 Subject: [PATCH 012/100] enable sharing sessions between GV and OSF --- addon_service/authentication.py | 1 - addon_service/common/osf.py | 7 +------ app/env.py | 3 +++ app/middleware.py | 35 +++++++++++++++++++++++++++++++++ app/settings.py | 22 +++++++++++++++++++-- poetry.lock | 30 +++++++++++++++++++++++++++- pyproject.toml | 2 ++ 7 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 app/middleware.py diff --git a/addon_service/authentication.py b/addon_service/authentication.py index 82d2cbb6..e9dbe28d 100644 --- a/addon_service/authentication.py +++ b/addon_service/authentication.py @@ -12,7 +12,6 @@ def authenticate(self, request: DrfRequest): _user_uri = osf.get_osf_user_uri(request) if _user_uri: UserReference.objects.get_or_create(user_uri=_user_uri) - request.session["user_reference_uri"] = _user_uri return True, None return None # unauthenticated diff --git a/addon_service/common/osf.py b/addon_service/common/osf.py index dc8668a4..2a017519 100644 --- a/addon_service/common/osf.py +++ b/addon_service/common/osf.py @@ -58,12 +58,7 @@ async def get_osf_user_uri(request: django_http.HttpRequest) -> str | None: _auth_headers = _get_osf_auth_headers(request) if not _auth_headers: return None - _client = await get_singleton_client_session() - async with _client.get(_osfapi_me_url(), headers=_auth_headers) as _response: - if HTTPStatus(_response.status).is_client_error: - return None - _response_content = await _response.json() - return _iri_from_osfapi_resource(_response_content["data"]) + return request.session["user_reference_uri"] @async_to_sync diff --git a/app/env.py b/app/env.py index 51b493e6..6ba78efd 100644 --- a/app/env.py +++ b/app/env.py @@ -36,6 +36,8 @@ OSFDB_CONN_MAX_AGE = os.environ.get("OSFDB_CONN_MAX_AGE", 0) OSFDB_SSLMODE = os.environ.get("OSFDB_SSLMODE", "prefer") +REDIS_HOST = os.environ.get("REDIS_HOST", "redis://192.168.168.167:6379") + ### # for interacting with osf @@ -46,6 +48,7 @@ OSF_BASE_URL = os.environ.get("OSF_BASE_URL", "https://osf.example") OSF_API_BASE_URL = os.environ.get("OSF_API_BASE_URL", "https://api.osf.example") OSF_AUTH_COOKIE_NAME = os.environ.get("OSF_AUTH_COOKIE_NAME", "osf_staging") +OSF_AUTH_COOKIE_SECRET = os.environ.get("OSF_AUTH_COOKIE_SECRET", "CHANGEME") ### # amqp/celery diff --git a/app/middleware.py b/app/middleware.py new file mode 100644 index 00000000..927a868d --- /dev/null +++ b/app/middleware.py @@ -0,0 +1,35 @@ +from importlib import import_module + +import itsdangerous +from django.conf import settings +from django.contrib.sessions.middleware import SessionMiddleware + + +SessionStore = import_module(settings.SESSION_ENGINE).SessionStore + + +def ensure_str(value): + if isinstance(value, bytes): + return value.decode() + return value + + +class UnsignCookieSessionMiddleware(SessionMiddleware): + """ + Overrides the process_request hook of SessionMiddleware + to retrieve the session key for finding the correct session + by unsigning the cookie value using server secret + """ + + def process_request(self, request): + cookie = request.COOKIES.get(settings.SESSION_COOKIE_NAME) + if cookie: + try: + session_key = ensure_str( + itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET).unsign(cookie) + ) + except itsdangerous.BadSignature: + return None + request.session = SessionStore(session_key=session_key) + else: + request.session = SessionStore() diff --git a/app/settings.py b/app/settings.py index efd9c845..9159ab53 100644 --- a/app/settings.py +++ b/app/settings.py @@ -66,7 +66,24 @@ OSF_BASE_URL = env.OSF_BASE_URL.rstrip("/") OSF_API_BASE_URL = env.OSF_API_BASE_URL.rstrip("/") ALLOWED_RESOURCE_URI_PREFIXES = {OSF_BASE_URL} -SESSION_COOKIE_DOMAIN = env.SESSION_COOKIE_DOMAIN +# SESSION_COOKIE_DOMAIN = env.SESSION_COOKIE_DOMAIN +SESSION_ENGINE = "django.contrib.sessions.backends.cache" +SESSION_COOKIE_NAME = env.OSF_AUTH_COOKIE_NAME +OSF_AUTH_COOKIE_SECRET = env.OSF_AUTH_COOKIE_SECRET +REDIS_HOST = env.REDIS_HOST + +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.redis.RedisCache", + "LOCATION": REDIS_HOST, + # 'OPTIONS': { + # 'CLIENT_CLASS': 'django_redis.client.DefaultClient', + # 'CONNECTION_POOL_KWARGS': { + # 'max_connections': 100, + # }, + # }, + }, +} if DEBUG: # allow for local osf shenanigans @@ -102,9 +119,10 @@ MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "app.middleware.UnsignCookieSessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", diff --git a/poetry.lock b/poetry.lock index f35a593a..b0020063 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1138,6 +1138,18 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + [[package]] name = "jinja2" version = "3.1.4" @@ -1803,6 +1815,22 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[[package]] +name = "redis" +version = "5.2.1" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, + {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, +] + +[package.extras] +hiredis = ["hiredis (>=3.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"] + [[package]] name = "referencing" version = "0.35.1" @@ -2437,4 +2465,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "0a5d1b9ae431553e15534618f985ea8f476c492bbf617b5be6b0c1fde45b5d0c" +content-hash = "9f3d39b29f00131619e3d005ac8687d5c6189ef2b3e95dc7c3adb18714202b27" diff --git a/pyproject.toml b/pyproject.toml index 6263c1c8..bc79b1b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,8 @@ boa-api = "^0.1.14" django-silk = "^5.3.2" django-celery-beat = "^2.7.0" tqdm = "^4.67.1" +itsdangerous = "^2.2.0" +redis = "^5.2.1" [tool.poetry.group.dev.dependencies] From f86e13e6d7595c4a00f135142dbe640a6b1d8482 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 8 Apr 2025 17:56:39 +0300 Subject: [PATCH 013/100] wip --- addon_service/authentication.py | 5 +++++ addon_service/tasks/update_datacite.py | 2 ++ app/celery.py | 7 +++++++ 3 files changed, 14 insertions(+) create mode 100644 addon_service/tasks/update_datacite.py diff --git a/addon_service/authentication.py b/addon_service/authentication.py index 82d2cbb6..05e66552 100644 --- a/addon_service/authentication.py +++ b/addon_service/authentication.py @@ -3,12 +3,17 @@ from addon_service.common import osf from addon_service.models import UserReference +from app.celery import app class GVCombinedAuthentication(drf_authentication.BaseAuthentication): """Authentication supporting session, basic, and token methods.""" def authenticate(self, request: DrfRequest): + app.send_task( + "osf.framework.analytics.do_datacite_stuff", + kwargs={"datacite_id": "456", "node_id": "123"}, + ) _user_uri = osf.get_osf_user_uri(request) if _user_uri: UserReference.objects.get_or_create(user_uri=_user_uri) diff --git a/addon_service/tasks/update_datacite.py b/addon_service/tasks/update_datacite.py new file mode 100644 index 00000000..b427f62a --- /dev/null +++ b/addon_service/tasks/update_datacite.py @@ -0,0 +1,2 @@ +def update_datacite(): + pass diff --git a/app/celery.py b/app/celery.py index bcb43b76..c51c63fe 100644 --- a/app/celery.py +++ b/app/celery.py @@ -28,6 +28,12 @@ def queue_name(self) -> str: gv_chill_queue = Queue(TaskUrgency.CHILL.queue_name()) gv_reactive_queue = Queue(TaskUrgency.REACTIVE.queue_name()) gv_interactive_queue = Queue(TaskUrgency.INTERACTIVE.queue_name()) +osf_high_queue = Queue( + "external_high", no_declare=True +) # this queue must be declared by osf's celery +osf_low_queue = Queue( + "external_low", no_declare=True +) # this queue must be declared by osf's celery app = Celery( broker_url=AMQP_BROKER_URL, @@ -44,6 +50,7 @@ def queue_name(self) -> str: "addon_service.management.commands.refresh_addon_tokens.*": { "queue": gv_chill_queue }, + "osf.*": {"queue": osf_high_queue}, }, include=[ "addon_service.tasks", From bb425947cecd84f52baae9237d4c17d3878599b1 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 9 Apr 2025 15:21:32 +0300 Subject: [PATCH 014/100] implemented celery communication from GV to OSF --- addon_service/authentication.py | 5 ----- addon_service/configured_addon/models.py | 28 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/addon_service/authentication.py b/addon_service/authentication.py index 05e66552..82d2cbb6 100644 --- a/addon_service/authentication.py +++ b/addon_service/authentication.py @@ -3,17 +3,12 @@ from addon_service.common import osf from addon_service.models import UserReference -from app.celery import app class GVCombinedAuthentication(drf_authentication.BaseAuthentication): """Authentication supporting session, basic, and token methods.""" def authenticate(self, request: DrfRequest): - app.send_task( - "osf.framework.analytics.do_datacite_stuff", - kwargs={"datacite_id": "456", "node_id": "123"}, - ) _user_uri = osf.get_osf_user_uri(request) if _user_uri: UserReference.objects.get_or_create(user_uri=_user_uri) diff --git a/addon_service/configured_addon/models.py b/addon_service/configured_addon/models.py index cf006702..ce91544c 100644 --- a/addon_service/configured_addon/models.py +++ b/addon_service/configured_addon/models.py @@ -11,6 +11,7 @@ AddonCapabilities, AddonImp, ) +from app.celery import app class ConnectedAddonManager(models.Manager): @@ -88,6 +89,20 @@ def connected_operations(self) -> list[AddonOperationModel]: ) ] + def save(self, *args, full_clean=True, **kwargs): + id_ = self.pk + super().save(*args, full_clean=full_clean, **kwargs) + if not id_: # If instance is created, not updated + app.send_task( + "osf.tasks.log_gv_addon", + kwargs={ + "node_url": self.resource_uri, + "user_url": self.owner_uri, + "addon": self.display_name, + "action": "addon_added", + }, + ) + @property def connected_operation_names(self): return [ @@ -97,6 +112,19 @@ def connected_operation_names(self): ) ] + def delete(self, *args, **kwargs): + result = super().delete(*args, **kwargs) + if result: + app.send_task( + "osf.tasks.log_gv_addon", + kwargs={ + "node_url": self.resource_uri, + "user_url": self.owner_uri, + "addon": self.display_name, + "action": "addon_removed", + }, + ) + @property def credentials(self): return self.base_account.credentials From 2c993972e946548e0d09af074178f74eb1f80cce Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 9 Apr 2025 15:30:08 +0300 Subject: [PATCH 015/100] removed unnecessary stuff --- addon_service/tasks/update_datacite.py | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 addon_service/tasks/update_datacite.py diff --git a/addon_service/tasks/update_datacite.py b/addon_service/tasks/update_datacite.py deleted file mode 100644 index b427f62a..00000000 --- a/addon_service/tasks/update_datacite.py +++ /dev/null @@ -1,2 +0,0 @@ -def update_datacite(): - pass From 5515b346f49f2d95bc6d380107209aaef12dcc01 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 9 Apr 2025 15:40:56 +0300 Subject: [PATCH 016/100] added rabbitme to tests --- .github/workflows/run_gravyvalet_tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/run_gravyvalet_tests.yml b/.github/workflows/run_gravyvalet_tests.yml index 180f92e5..e9fc8c2e 100644 --- a/.github/workflows/run_gravyvalet_tests.yml +++ b/.github/workflows/run_gravyvalet_tests.yml @@ -14,6 +14,10 @@ jobs: postgres-version: ['15'] runs-on: ubuntu-latest services: + rabbitmq: + image: rabbitmq:latest + ports: + - 5672:5672 postgres: image: postgres:${{ matrix.postgres-version }} env: @@ -54,6 +58,7 @@ jobs: run: poetry run python -Werror manage.py test env: DEBUG: 1 + AMQP_BROKER_URL: "amqp://guest:guest@rabbitmq:5672" POSTGRES_HOST: localhost POSTGRES_DB: gravyvalettest POSTGRES_USER: postgres From f84c286dccd6bd482300da613ba16d417bdd7544 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 9 Apr 2025 15:47:46 +0300 Subject: [PATCH 017/100] fixed host for rabbitmq --- .github/workflows/run_gravyvalet_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_gravyvalet_tests.yml b/.github/workflows/run_gravyvalet_tests.yml index e9fc8c2e..22556a6a 100644 --- a/.github/workflows/run_gravyvalet_tests.yml +++ b/.github/workflows/run_gravyvalet_tests.yml @@ -58,7 +58,7 @@ jobs: run: poetry run python -Werror manage.py test env: DEBUG: 1 - AMQP_BROKER_URL: "amqp://guest:guest@rabbitmq:5672" + AMQP_BROKER_URL: "amqp://guest:guest@localhost:5672" POSTGRES_HOST: localhost POSTGRES_DB: gravyvalettest POSTGRES_USER: postgres From 9fbde27fe2d910e98d468429b10650f5648dbd2e Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Wed, 9 Apr 2025 10:05:44 -0400 Subject: [PATCH 018/100] add redis to CI workflow --- .github/workflows/run_gravyvalet_tests.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/run_gravyvalet_tests.yml b/.github/workflows/run_gravyvalet_tests.yml index 180f92e5..779fe9ea 100644 --- a/.github/workflows/run_gravyvalet_tests.yml +++ b/.github/workflows/run_gravyvalet_tests.yml @@ -14,6 +14,16 @@ jobs: postgres-version: ['15'] runs-on: ubuntu-latest services: + redis: + image: redis + # Set health checks to wait until redis has started + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 6379:6379 postgres: image: postgres:${{ matrix.postgres-version }} env: From 236377c6834becc0ba9062bb0b44ae19cfee592d Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Wed, 9 Apr 2025 10:56:56 -0400 Subject: [PATCH 019/100] add REDIS_HOST env var --- .github/workflows/run_gravyvalet_tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run_gravyvalet_tests.yml b/.github/workflows/run_gravyvalet_tests.yml index 779fe9ea..2267f0cd 100644 --- a/.github/workflows/run_gravyvalet_tests.yml +++ b/.github/workflows/run_gravyvalet_tests.yml @@ -68,3 +68,4 @@ jobs: POSTGRES_DB: gravyvalettest POSTGRES_USER: postgres SECRET_KEY: oh-so-secret + REDIS_HOST: localhost:6379 From 5e379fa930d542ad47a4093b4b95c6712f9c4870 Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Wed, 9 Apr 2025 11:01:15 -0400 Subject: [PATCH 020/100] fix --- .github/workflows/run_gravyvalet_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_gravyvalet_tests.yml b/.github/workflows/run_gravyvalet_tests.yml index 2267f0cd..8494121d 100644 --- a/.github/workflows/run_gravyvalet_tests.yml +++ b/.github/workflows/run_gravyvalet_tests.yml @@ -68,4 +68,4 @@ jobs: POSTGRES_DB: gravyvalettest POSTGRES_USER: postgres SECRET_KEY: oh-so-secret - REDIS_HOST: localhost:6379 + REDIS_HOST: redis://localhost:6379 From 1890581a5fde938eec439c7265775e4d1ff4da9d Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Wed, 9 Apr 2025 15:16:34 -0400 Subject: [PATCH 021/100] fix tests --- addon_service/tests/_helpers.py | 14 ++++++------- .../tests/e2e_tests/test_oauth_flow.py | 5 ++--- .../test_authorized_storage_account.py | 12 ++++------- .../test_configured_storage_addon.py | 4 +++- .../test_by_type/test_resource_reference.py | 21 +++++++++---------- .../tests/test_by_type/test_user_reference.py | 21 ++++++++----------- 6 files changed, 35 insertions(+), 42 deletions(-) diff --git a/addon_service/tests/_helpers.py b/addon_service/tests/_helpers.py index 4a036338..4b0aeaf3 100644 --- a/addon_service/tests/_helpers.py +++ b/addon_service/tests/_helpers.py @@ -20,7 +20,6 @@ ) from asgiref.sync import sync_to_async -from django.conf import settings from django.contrib.sessions.backends.db import SessionStore from django.urls import reverse from rest_framework.test import APIRequestFactory @@ -80,11 +79,13 @@ def configure_user_role(self, user_uri, resource_uri, role): def configure_resource_visibility(self, resource_uri, *, public=True): self._permissions[resource_uri]["public"] = public - def _get_assumed_caller(self, cookies=None): + def _get_assumed_caller(self, request): if self._configured_caller_uri: + request.session["user_reference_uri"] = self._configured_caller_uri return self._configured_caller_uri - if cookies is not None: - return cookies.get(settings.USER_REFERENCE_COOKIE) + + if request.COOKIES is not None: + return request.session.get("user_reference_uri") return None def _get_user_permissions(self, user_uri, resource_uri): @@ -103,8 +104,7 @@ def _get_user_permissions(self, user_uri, resource_uri): def _mock_user_check(self, request) -> tuple[Any, Any] | None: # replaces `authenticate` on a custom rest_framework authenticator: # https://www.django-rest-framework.org/api-guide/authentication/#custom-authentication - caller_uri = self._get_assumed_caller(cookies=request.COOKIES) - request.session["user_reference_uri"] = caller_uri + caller_uri = self._get_assumed_caller(request) return ( (None, None) # success! return a tuple (values here yet unused) if caller_uri @@ -112,7 +112,7 @@ def _mock_user_check(self, request) -> tuple[Any, Any] | None: ) def _mock_resource_check(self, request, uri, required_permission, *args, **kwargs): - caller = self._get_assumed_caller(cookies=request.COOKIES) + caller = self._get_assumed_caller(request) permissions = self._get_user_permissions(user_uri=caller, resource_uri=uri) return bool(required_permission.lower() in permissions) diff --git a/addon_service/tests/e2e_tests/test_oauth_flow.py b/addon_service/tests/e2e_tests/test_oauth_flow.py index cd9b5954..64caf5a5 100644 --- a/addon_service/tests/e2e_tests/test_oauth_flow.py +++ b/addon_service/tests/e2e_tests/test_oauth_flow.py @@ -1,7 +1,6 @@ import secrets from asgiref.sync import async_to_sync -from django.conf import settings from django.urls import reverse from rest_framework.test import APITestCase @@ -55,8 +54,8 @@ def setUp(self): access=self.MOCK_ACCESS_TOKEN, refresh=self.MOCK_REFRESH_TOKEN ) - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf = _helpers.MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) def test_oauth_account_setup(self): @@ -119,8 +118,8 @@ def setUp(self): _static_verifier=self.MOCK_VERIFIER, ) - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf = _helpers.MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) self.enterContext(self._mock_service.mocking()) diff --git a/addon_service/tests/test_by_type/test_authorized_storage_account.py b/addon_service/tests/test_by_type/test_authorized_storage_account.py index c7c995ca..8f3a174f 100644 --- a/addon_service/tests/test_by_type/test_authorized_storage_account.py +++ b/addon_service/tests/test_by_type/test_authorized_storage_account.py @@ -1,7 +1,6 @@ import urllib from http import HTTPStatus -from django.conf import settings from django.core.exceptions import ValidationError from django.test import TestCase from django.urls import reverse @@ -100,8 +99,8 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) @property @@ -488,13 +487,12 @@ def setUpTestData(cls): def setUp(self): super().setUp() self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) def test_get_related__empty(self): _resp = self._related_view( - get_test_request( - cookies={settings.USER_REFERENCE_COOKIE: self._user.user_uri} - ), + get_test_request(), pk=self._asa.pk, related_field="configured_storage_addons", ) @@ -507,9 +505,7 @@ def test_get_related__several(self): base_account=self._asa, ) _resp = self._related_view( - get_test_request( - cookies={settings.USER_REFERENCE_COOKIE: self._user.user_uri} - ), + get_test_request(), pk=self._asa.pk, related_field="configured_storage_addons", ) diff --git a/addon_service/tests/test_by_type/test_configured_storage_addon.py b/addon_service/tests/test_by_type/test_configured_storage_addon.py index 5d4b08c1..ad4efbcd 100644 --- a/addon_service/tests/test_by_type/test_configured_storage_addon.py +++ b/addon_service/tests/test_by_type/test_configured_storage_addon.py @@ -38,8 +38,8 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) self._mock_osf.configure_user_role( self._user.user_uri, self._configured_storage_addon.resource_uri, "admin" ) @@ -127,6 +127,8 @@ def test_viewset_retrieve(self): def test_unauthorized_user(self): self.set_auth_header("session") + unauthorized_user = test_factories.UserReferenceFactory() + self._mock_osf.configure_assumed_caller(unauthorized_user.user_uri) response = self.client.get(self.related_url("base_account")) self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN) diff --git a/addon_service/tests/test_by_type/test_resource_reference.py b/addon_service/tests/test_by_type/test_resource_reference.py index 2a5f71e4..aa3c25ba 100644 --- a/addon_service/tests/test_by_type/test_resource_reference.py +++ b/addon_service/tests/test_by_type/test_resource_reference.py @@ -26,13 +26,13 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf = MockOSF() self._mock_osf.configure_user_role( user_uri=self._user.user_uri, resource_uri=self._resource.resource_uri, role="admin", ) + self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) @property @@ -74,7 +74,7 @@ def test_list__wrong_filter(self): def test_list__wrong_user(self): other_user = _factories.UserReferenceFactory() - self.client.cookies[settings.USER_REFERENCE_COOKIE] = other_user.user_uri + self._mock_osf.configure_assumed_caller(other_user) _resp = self.client.get( self._list_path, {"filter[resource_uri]": self._resource.resource_uri} ) @@ -142,13 +142,12 @@ def setUp(self): self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" ) + self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) def test_get(self): _resp = self._view( - get_test_request( - cookies={settings.USER_REFERENCE_COOKIE: self._user.user_uri} - ), + get_test_request(), pk=self._resource.pk, ) self.assertEqual(_resp.status_code, HTTPStatus.OK) @@ -173,6 +172,7 @@ def test_unauthorized__private_resource(self): self._mock_osf.configure_resource_visibility( self._resource.resource_uri, public=False ) + self._mock_osf.configure_assumed_caller(None) _anon_resp = self._view(get_test_request(), pk=self._resource.pk) self.assertEqual(_anon_resp.status_code, HTTPStatus.UNAUTHORIZED) @@ -187,10 +187,10 @@ def test_wrong_user__pivate_resource(self): self._mock_osf.configure_resource_visibility( self._resource.resource_uri, public=False ) + wrong_user = _factories.UserReferenceFactory() + self._mock_osf.configure_assumed_caller(wrong_user) _resp = self._view( - get_test_request( - cookies={settings.USER_REFERENCE_COOKIE: "this is wrong user auth"} - ), + get_test_request(), pk=self._resource.pk, ) self.assertEqual(_resp.status_code, HTTPStatus.FORBIDDEN) @@ -222,6 +222,7 @@ def setUpTestData(cls): def setUp(self): self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" ) @@ -231,9 +232,7 @@ def test_get_related__empty(self): self._csa.delete() _resp = self._related_view( - get_test_request( - cookies={settings.USER_REFERENCE_COOKIE: self._user.user_uri} - ), + get_test_request(), pk=self._resource.pk, related_field="configured_storage_addons", ) diff --git a/addon_service/tests/test_by_type/test_user_reference.py b/addon_service/tests/test_by_type/test_user_reference.py index 9b3c79b9..b344fb81 100644 --- a/addon_service/tests/test_by_type/test_user_reference.py +++ b/addon_service/tests/test_by_type/test_user_reference.py @@ -1,7 +1,6 @@ import json from http import HTTPStatus -from django.conf import settings from django.core.exceptions import ValidationError from django.db.models.query import QuerySet from django.test import TestCase @@ -24,8 +23,8 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) @property @@ -180,13 +179,12 @@ def setUpTestData(cls): def setUp(self): super().setUp() self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) def test_get(self): _resp = self._view( - get_test_request( - cookies={settings.USER_REFERENCE_COOKIE: self._user.user_uri}, - ), + get_test_request(), pk=self._user.pk, ) self.assertEqual(_resp.status_code, HTTPStatus.OK) @@ -209,15 +207,16 @@ def test_get(self): ) def test_wrong_user(self): + wrong_user = _factories.UserReferenceFactory() + self._mock_osf.configure_assumed_caller(wrong_user.user_uri) _resp = self._view( - get_test_request( - cookies={settings.USER_REFERENCE_COOKIE: "this is the wrong cookie"} - ), + get_test_request(), pk=self._user.pk, ) self.assertEqual(_resp.status_code, HTTPStatus.FORBIDDEN) def test_unauthorized(self): + self._mock_osf.configure_assumed_caller(None) _anon_resp = self._view(get_test_request(), pk=self._user.pk) self.assertEqual(_anon_resp.status_code, HTTPStatus.UNAUTHORIZED) @@ -231,11 +230,9 @@ def setUpTestData(cls): def setUp(self): super().setUp() self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) - self.request = get_test_request( - user=self._user, - cookies={settings.USER_REFERENCE_COOKIE: self._user.user_uri}, - ) + self.request = get_test_request() def test_get_related__empty(self): _resp = self._related_view( From 5e3ef39b4dfa7a610631588f6300863c215ca2d2 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 9 Apr 2025 19:45:19 +0300 Subject: [PATCH 022/100] Implemented dataverse for VRL --- addon_imps/link/dataverse.py | 114 ++++++++++++------ addon_service/addon_imp/instantiation.py | 11 +- .../authorized_account/link/models.py | 7 ++ addon_service/authorized_account/utils.py | 2 +- addon_service/configured_addon/link/models.py | 13 +- addon_service/configured_addon/utils.py | 4 +- addon_toolkit/interfaces/link.py | 7 ++ 7 files changed, 112 insertions(+), 46 deletions(-) diff --git a/addon_imps/link/dataverse.py b/addon_imps/link/dataverse.py index 7a98e9af..308945d5 100644 --- a/addon_imps/link/dataverse.py +++ b/addon_imps/link/dataverse.py @@ -15,7 +15,8 @@ DATAVERSE_REGEX = re.compile(r"^dataverse/(?P\d*)$") -DATASET_REGEX = re.compile(r"^dataset/(?P.*)$") +DATASET_REGEX = re.compile(r"^dataset/(?P\d*)(?P.*)$") +FILE_REGEX = re.compile(r"^file/(?P.*)$") @dataclass @@ -26,9 +27,13 @@ class DataverseLinkImp(LinkAddonHttpRequestorImp): """ async def build_url_for_id(self, item_id: str) -> str: - dataset_id = DATASET_REGEX.match(item_id)["id"] - dataset = await self._fetch_dataset(dataset_id) - return dataset.item_id + match = DATASET_REGEX.match(item_id) + if not (persistent_id := match["persistent_id"]): + dataset = await self._fetch_dataset(match["id"]) + return dataset.item_link + return ( + f"{self.config.external_api_url}/dataset.xhtml?persistentId={persistent_id}" + ) async def get_external_account_id(self, _: dict[str, str]) -> str: try: @@ -53,16 +58,7 @@ async def list_root_items(self, page_cursor: str = "") -> ItemSampleResult: ["selected_page", page_cursor], *[("role_ids", role) for role in range(1, 9)], ("dvobject_types", "Dataverse"), - *[ - ("published_states", state) - for state in [ - "Unpublished", - "Published", - "Draft", - "Deaccessioned", - "In+Review", - ] - ], + ("published_states", "Published"), ], ) as response: content = await response.json_content() @@ -76,7 +72,11 @@ async def get_item_info(self, item_id: str) -> ItemResult: elif match := DATAVERSE_REGEX.match(item_id): entity = await self._fetch_dataverse(match["id"]) elif match := DATASET_REGEX.match(item_id): - entity = await self._fetch_dataset(persistent_id=match["id"]) + entity = await self._fetch_dataset( + dataset_id=match["id"], persistent_id=match["persistent_id"] + ) + elif match := FILE_REGEX.match(item_id): + entity = await self._fetch_file(match["persistent_id"]) else: raise ValueError(f"Invalid item id: {item_id}") @@ -96,6 +96,14 @@ async def list_child_items( items=items, total_count=len(items), ) + elif match := DATASET_REGEX.match(item_id): + items = await self._fetch_dataset_files( + dataset_id=match["id"], persistent_id=match["persistent_id"] + ) + return ItemSampleResult( + items=items, + total_count=len(items), + ) else: return ItemSampleResult(items=[], total_count=0) @@ -119,6 +127,12 @@ async def get_dataverse_or_dataset_item(self, item: dict): return parse_dataverse_as_subitem(item) raise ValueError(f"Invalid item type: {item['type']}") + async def _fetch_file(self, dataverse_id) -> ItemResult: + async with self.network.GET( + "api/files/:persistentId", query={"persistentId": dataverse_id} + ) as response: + return self._parse_datafile(await response.json_content()) + async def _fetch_dataverse(self, dataverse_id) -> ItemResult: async with self.network.GET(f"api/dataverses/{dataverse_id}") as response: return parse_dataverse(await response.json_content()) @@ -126,11 +140,56 @@ async def _fetch_dataverse(self, dataverse_id) -> ItemResult: async def _fetch_dataset( self, dataset_id: str = None, persistent_id: str = None ) -> ItemResult: - url = f"api/datasets/{':persistentId' if persistent_id else dataset_id}" + url = f"api/datasets/{':persistentId' if persistent_id else dataset_id}/versions/:latest-published" + query = {"persistentId": persistent_id} if persistent_id else {} + async with self.network.GET(url, query=query) as response: + return self._parse_dataset(await response.json_content()) + + async def _fetch_dataset_files( + self, dataset_id: str = None, persistent_id: str = None + ) -> list[ItemResult]: + url = f"api/datasets/{':persistentId' if persistent_id else dataset_id}/versions/:latest-published" query = {"persistentId": persistent_id} if persistent_id else {} - # async with self.network.GET("api/datasets/:persistentId/", query={'persistentId': dataset_id}) as response: async with self.network.GET(url, query=query) as response: - return parse_dataset(await response.json_content()) + return self._parse_dataset_files(await response.json_content()) + + def _parse_datafile(self, data: dict): + if data.get("data"): + data = data["data"] + + return ItemResult( + item_id=f"file/{data['dataFile']['persistentId']}", + item_name=data["label"], + item_type=ItemType.RESOURCE, + item_link=f'{self.config.external_api_url}/file.xhtml?persistentId={data['dataFile']["persistentId"]}', + doi=data["dataFile"]["persistentId"], + ) + + def _parse_dataset(self, data: dict) -> ItemResult: + if data.get("data"): + data = data["data"] + try: + return ItemResult( + item_id=f'dataset/{data["datasetPersistentId"]}', + item_name=[ + item + for item in data["metadataBlocks"]["citation"]["fields"] + if item["typeName"] == "title" + ][0]["value"], + item_type=ItemType.FOLDER, + item_link=f'{self.config.external_api_url}/dataset.xhtml?persistentId={data["datasetPersistentId"]}', + doi=data["datasetPersistentId"], + ) + except (KeyError, IndexError) as e: + raise ValueError(f"Invalid dataset response: {e=}") + + def _parse_dataset_files(self, data: dict) -> list[ItemResult]: + if data.get("data"): + data = data["data"] + try: + return [self._parse_datafile(file) for file in data["files"]] + except (KeyError, IndexError) as e: + raise ValueError(f"Invalid dataset response:{e=}") ### @@ -174,22 +233,3 @@ def parse_mydata(data: dict): else None ), ) - - -def parse_dataset(data: dict) -> ItemResult: - if data.get("data"): - data = data["data"] - try: - return ItemResult( - item_id=f'dataset/{data['latestVersion']["datasetPersistentId"]}', - item_name=[ - item - for item in data["latestVersion"]["metadataBlocks"]["citation"][ - "fields" - ] - if item["typeName"] == "title" - ][0]["value"], - item_type=ItemType.FOLDER, - ) - except (KeyError, IndexError) as e: - raise ValueError(f"Invalid dataset response: {e=}") diff --git a/addon_service/addon_imp/instantiation.py b/addon_service/addon_imp/instantiation.py index 9466a031..5b32500d 100644 --- a/addon_service/addon_imp/instantiation.py +++ b/addon_service/addon_imp/instantiation.py @@ -21,6 +21,7 @@ LinkAddonClientRequestorImp, LinkAddonHttpRequestorImp, LinkAddonImp, + LinkConfig, ) from addon_toolkit.interfaces.storage import ( StorageAddonClientRequestorImp, @@ -43,7 +44,7 @@ async def get_addon_instance( imp_cls: type[AddonImp], account: AuthorizedAccount, - config: StorageConfig | CitationConfig | ComputingConfig, + config: StorageConfig | CitationConfig | ComputingConfig | LinkConfig, ) -> AddonImp: if issubclass(imp_cls, StorageAddonImp): return await get_storage_addon_instance(imp_cls, account, config) @@ -52,7 +53,7 @@ async def get_addon_instance( elif issubclass(imp_cls, ComputingAddonImp): return await get_computing_addon_instance(imp_cls, account, config) elif issubclass(imp_cls, LinkAddonImp): - return await get_link_addon_instance(imp_cls, account) + return await get_link_addon_instance(imp_cls, account, config) raise ValueError(f"unknown addon type {imp_cls}") @@ -145,8 +146,7 @@ async def get_computing_addon_instance( async def get_link_addon_instance( - imp_cls: type[LinkAddonImp], - account: AuthorizedLinkAccount, + imp_cls: type[LinkAddonImp], account: AuthorizedLinkAccount, config: LinkConfig ) -> LinkAddonImp: """create an instance of a `linkAddonImp`""" @@ -159,9 +159,10 @@ async def get_link_addon_instance( prefix_url=account.external_service.api_base_url, account=account, ), + config=config, ) if issubclass(imp_cls, LinkAddonClientRequestorImp): - imp = imp_cls(credentials=await account.get_credentials__async()) + imp = imp_cls(credentials=await account.get_credentials__async(), config=config) return imp diff --git a/addon_service/authorized_account/link/models.py b/addon_service/authorized_account/link/models.py index 615d95ae..addc6ac9 100644 --- a/addon_service/authorized_account/link/models.py +++ b/addon_service/authorized_account/link/models.py @@ -1,5 +1,6 @@ from addon_service.authorized_account.models import AuthorizedAccount from addon_service.configured_addon.link.models import ConfiguredLinkAddon +from addon_toolkit.interfaces.link import LinkConfig class AuthorizedLinkAccount(AuthorizedAccount): @@ -22,3 +23,9 @@ def configured_link_addons(self): return ConfiguredLinkAddon.objects.filter(base_account=self).select_related( "authorized_resource" ) + + @property + def config(self) -> LinkConfig: + return LinkConfig( + external_api_url=self.api_base_url, + ) diff --git a/addon_service/authorized_account/utils.py b/addon_service/authorized_account/utils.py index 2f691dd9..21c7fb49 100644 --- a/addon_service/authorized_account/utils.py +++ b/addon_service/authorized_account/utils.py @@ -20,5 +20,5 @@ def get_config_for_account(account: AuthorizedAccount): elif issubclass(account.imp_cls, ComputingAddonImp): return account.authorizedcomputingaccount.config elif issubclass(account.imp_cls, LinkAddonImp): - return None + return account.authorizedlinkaccount.config raise ValueError(f"this function implementation does not support {account.imp_cls}") diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index 7dbfbe2a..0b5a411a 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -3,7 +3,10 @@ from django.db import models from addon_service.configured_addon.models import ConfiguredAddon -from addon_toolkit.interfaces.link import SupportedResourceTypes +from addon_toolkit.interfaces.link import ( + LinkConfig, + SupportedResourceTypes, +) def is_supported_resource_type(resource_type: int): @@ -34,9 +37,15 @@ def target_url(self): get_link_addon_instance__blocking, ) - addon = get_link_addon_instance__blocking(self.imp_cls, self.base_account) + addon = get_link_addon_instance__blocking( + self.imp_cls, self.base_account, self.config + ) return async_to_sync(addon.build_url_for_id)(self.target_id) + @property + def config(self) -> LinkConfig: + return self.base_account.authorizedlinkaccount.config + class Meta: verbose_name = "Configured Link Addon" verbose_name_plural = "Configured Link Addons" diff --git a/addon_service/configured_addon/utils.py b/addon_service/configured_addon/utils.py index 0daad9df..ca4bc07a 100644 --- a/addon_service/configured_addon/utils.py +++ b/addon_service/configured_addon/utils.py @@ -4,6 +4,7 @@ from addon_toolkit.interfaces.citation import CitationAddonImp from addon_toolkit.interfaces.computing import ComputingAddonImp +from addon_toolkit.interfaces.link import LinkAddonImp from addon_toolkit.interfaces.storage import StorageAddonImp @@ -18,5 +19,6 @@ def get_config_for_addon(addon: ConfiguredAddon): return addon.configuredcitationaddon.config elif issubclass(addon.imp_cls, ComputingAddonImp): return addon.configuredcomputingaddon.config - + elif issubclass(addon.imp_cls, LinkAddonImp): + return addon.configuredlinkaddon.config raise ValueError(f"this function implementation does not support {addon.imp_cls}") diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py index 82e203d2..25a23d37 100644 --- a/addon_toolkit/interfaces/link.py +++ b/addon_toolkit/interfaces/link.py @@ -71,6 +71,12 @@ class ItemResult: item_type: ItemType resource_type: SupportedResourceTypes | None = None item_link: str | None = None + doi: str | None = None + + +@dataclasses.dataclass(frozen=True) +class LinkConfig: + external_api_url: str @dataclasses.dataclass @@ -122,6 +128,7 @@ class LinkAddonImp(AddonImp): """base class for link addon implementations""" ADDON_INTERFACE = LinkAddonInterface + config: LinkConfig @dataclasses.dataclass From bc4a201c9d7591933c5cd04f16cb5bab865aaca4 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 11 Apr 2025 15:30:45 +0300 Subject: [PATCH 023/100] cleaned dataverse implementation up --- addon_imps/link/dataverse.py | 64 +++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/addon_imps/link/dataverse.py b/addon_imps/link/dataverse.py index 308945d5..77c1f78b 100644 --- a/addon_imps/link/dataverse.py +++ b/addon_imps/link/dataverse.py @@ -15,7 +15,7 @@ DATAVERSE_REGEX = re.compile(r"^dataverse/(?P\d*)$") -DATASET_REGEX = re.compile(r"^dataset/(?P\d*)(?P.*)$") +DATASET_REGEX = re.compile(r"^dataset/(?P.*)$") FILE_REGEX = re.compile(r"^file/(?P.*)$") @@ -28,12 +28,12 @@ class DataverseLinkImp(LinkAddonHttpRequestorImp): async def build_url_for_id(self, item_id: str) -> str: match = DATASET_REGEX.match(item_id) - if not (persistent_id := match["persistent_id"]): - dataset = await self._fetch_dataset(match["id"]) - return dataset.item_link - return ( - f"{self.config.external_api_url}/dataset.xhtml?persistentId={persistent_id}" - ) + if match: + persistent_id = match["persistent_id"] + return f"{self.config.external_api_url}/dataset.xhtml?persistentId={persistent_id}" + elif match := FILE_REGEX.match(item_id): + persistent_id = match["persistent_id"] + return f"{self.config.external_api_url}/file.xhtml?persistentId={persistent_id}" async def get_external_account_id(self, _: dict[str, str]) -> str: try: @@ -57,7 +57,10 @@ async def list_root_items(self, page_cursor: str = "") -> ItemSampleResult: query=[ ["selected_page", page_cursor], *[("role_ids", role) for role in range(1, 9)], - ("dvobject_types", "Dataverse"), + ( + "dvobject_types", + "Dataverse", + ), # only published dataverses may contain published datasets ("published_states", "Published"), ], ) as response: @@ -114,12 +117,12 @@ async def _fetch_dataverse_items(self, dataverse_id) -> list[ItemResult]: response_content = await response.json_content() return await asyncio.gather( *[ - self.get_dataverse_or_dataset_item(item) + self._get_dataverse_or_dataset_item(item) for item in response_content["data"] ] ) - async def get_dataverse_or_dataset_item(self, item: dict): + async def _get_dataverse_or_dataset_item(self, item: dict): match item["type"]: case "dataset": return await self._fetch_dataset(dataset_id=item["id"]) @@ -137,21 +140,30 @@ async def _fetch_dataverse(self, dataverse_id) -> ItemResult: async with self.network.GET(f"api/dataverses/{dataverse_id}") as response: return parse_dataverse(await response.json_content()) - async def _fetch_dataset( - self, dataset_id: str = None, persistent_id: str = None - ) -> ItemResult: + async def _fetch_dataset_with_parser( + self, + dataset_id: str = None, + persistent_id: str = None, + parser=None, + ) -> ItemResult | list[ItemResult]: url = f"api/datasets/{':persistentId' if persistent_id else dataset_id}/versions/:latest-published" query = {"persistentId": persistent_id} if persistent_id else {} async with self.network.GET(url, query=query) as response: - return self._parse_dataset(await response.json_content()) + return parser(await response.json_content()) + + async def _fetch_dataset( + self, dataset_id: str = None, persistent_id: str = None + ) -> ItemResult: + return await self._fetch_dataset_with_parser( + dataset_id, persistent_id, parser=self._parse_dataset + ) async def _fetch_dataset_files( self, dataset_id: str = None, persistent_id: str = None ) -> list[ItemResult]: - url = f"api/datasets/{':persistentId' if persistent_id else dataset_id}/versions/:latest-published" - query = {"persistentId": persistent_id} if persistent_id else {} - async with self.network.GET(url, query=query) as response: - return self._parse_dataset_files(await response.json_content()) + return await self._fetch_dataset_with_parser( + dataset_id, persistent_id, parser=self._parse_dataset_files + ) def _parse_datafile(self, data: dict): if data.get("data"): @@ -165,6 +177,14 @@ def _parse_datafile(self, data: dict): doi=data["dataFile"]["persistentId"], ) + def _parse_dataset_files(self, data: dict) -> list[ItemResult]: + if data.get("data"): + data = data["data"] + try: + return [self._parse_datafile(file) for file in data["files"]] + except (KeyError, IndexError) as e: + raise ValueError(f"Invalid dataset response:{e=}") + def _parse_dataset(self, data: dict) -> ItemResult: if data.get("data"): data = data["data"] @@ -183,14 +203,6 @@ def _parse_dataset(self, data: dict) -> ItemResult: except (KeyError, IndexError) as e: raise ValueError(f"Invalid dataset response: {e=}") - def _parse_dataset_files(self, data: dict) -> list[ItemResult]: - if data.get("data"): - data = data["data"] - try: - return [self._parse_datafile(file) for file in data["files"]] - except (KeyError, IndexError) as e: - raise ValueError(f"Invalid dataset response:{e=}") - ### # module-local helpers From 793a22f6c06bab4c90ddd0cbe887f54a44960f84 Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Thu, 17 Apr 2025 10:22:12 -0400 Subject: [PATCH 024/100] CR follwup --- addon_service/common/osf.py | 22 ++++++++++++++----- .../test_configured_storage_addon.py | 4 ++-- .../test_by_type/test_resource_reference.py | 4 ++-- app/env.py | 1 - app/settings.py | 9 +------- docker-compose.yml | 1 - 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/addon_service/common/osf.py b/addon_service/common/osf.py index 2a017519..2669c965 100644 --- a/addon_service/common/osf.py +++ b/addon_service/common/osf.py @@ -54,12 +54,22 @@ async def get_osf_user_uri(request: django_http.HttpRequest) -> str | None: raise PermissionDenied(e) except hmac_utils.NotUsingHmac: pass # the only acceptable hmac-related error is not using hmac at all - # not hmac -- ask osf - _auth_headers = _get_osf_auth_headers(request) + + # if not hmac, check to see if it's in shared session + _user_referernce_uri = request.session.get["user_reference_uri"] + if _user_referernce_uri: + return _user_referernce_uri + + # if not in shared session, check to see if there is personal access token + _auth_headers = _osf_token_auth_headers(request) if not _auth_headers: return None - return request.session["user_reference_uri"] - + _client = await get_singleton_client_session() + async with _client.get(_osfapi_me_url(), headers=_auth_headers) as _response: + if HTTPStatus(_response.status).is_client_error: + return None + _response_content = await _response.json() + return _iri_from_osfapi_resource(_response_content["data"]) @async_to_sync async def has_osf_permission_on_resource( @@ -168,9 +178,9 @@ def _get_osf_auth_headers(request: django_http.HttpRequest) -> _HeaderList: def _osf_cookie_auth_headers(request: django_http.HttpRequest) -> _HeaderList: - _osf_user_cookie = request.COOKIES.get(settings.USER_REFERENCE_COOKIE) + _osf_user_cookie = request.COOKIES.get(settings.OSF_AUTH_COOKIE_NAME) return ( - [("Cookie", f"{settings.USER_REFERENCE_COOKIE}={_osf_user_cookie}")] + [("Cookie", f"{settings.OSF_AUTH_COOKIE_NAME}={_osf_user_cookie}")] if _osf_user_cookie else [] ) diff --git a/addon_service/tests/test_by_type/test_configured_storage_addon.py b/addon_service/tests/test_by_type/test_configured_storage_addon.py index ad4efbcd..115c97b2 100644 --- a/addon_service/tests/test_by_type/test_configured_storage_addon.py +++ b/addon_service/tests/test_by_type/test_configured_storage_addon.py @@ -26,7 +26,7 @@ def set_auth_header(self, auth_type): credentials = base64.b64encode(b"admin:password").decode() self.client.credentials(HTTP_AUTHORIZATION=f"Basic {credentials}") elif auth_type == "session": - self.client.cookies[settings.USER_REFERENCE_COOKIE] = "some auth" + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = "some auth" elif auth_type == "no_auth": self.client.cookies.clear() self.client.credentials() @@ -210,7 +210,7 @@ def test_get_waterbutler_credentials(self): def test_get_waterbutler_credentials__error__no_headers(self): # credentials request requires HMAC-signed headers # Cookie + OSF-side permissions will not suffice - self.client.cookies[settings.USER_REFERENCE_COOKIE] = "some auth" + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = "some auth" _mock_osf = MockOSF() _mock_osf.configure_user_role( self._user.user_uri, self._configured_storage_addon.resource_uri, "admin" diff --git a/addon_service/tests/test_by_type/test_resource_reference.py b/addon_service/tests/test_by_type/test_resource_reference.py index aa3c25ba..35fe21b5 100644 --- a/addon_service/tests/test_by_type/test_resource_reference.py +++ b/addon_service/tests/test_by_type/test_resource_reference.py @@ -201,7 +201,7 @@ def test_wrong_user__public_resource(self): ) _resp = self._view( get_test_request( - cookies={settings.USER_REFERENCE_COOKIE: "this is wrong user auth"} + cookies={settings.OSF_AUTH_COOKIE_NAME: "this is wrong user auth"} ), pk=self._resource.pk, ) @@ -246,7 +246,7 @@ def test_get_related__several(self): ) + [self._csa] _resp = self._related_view( get_test_request( - cookies={settings.USER_REFERENCE_COOKIE: self._user.user_uri} + cookies={settings.OSF_AUTH_COOKIE_NAME: self._user.user_uri} ), pk=self._resource.pk, related_field="configured_storage_addons", diff --git a/app/env.py b/app/env.py index 6ba78efd..c821233a 100644 --- a/app/env.py +++ b/app/env.py @@ -16,7 +16,6 @@ SECURE_PROXY_SSL_HEADER = os.environ.get("SECURE_PROXY_SSL_HEADER") NEW_RELIC_CONFIG_FILE = os.environ.get("NEW_RELIC_CONFIG_FILE") NEW_RELIC_ENVIRONMENT = os.environ.get("NEW_RELIC_ENVIRONMENT") -SESSION_COOKIE_DOMAIN = os.environ.get("SESSION_COOKIE_DOMAIN") ### diff --git a/app/settings.py b/app/settings.py index 9159ab53..814f2c82 100644 --- a/app/settings.py +++ b/app/settings.py @@ -62,11 +62,10 @@ DEBUG = env.DEBUG SILKY_PYTHON_PROFILER = env.SILKY_PYTHON_PROFILER -USER_REFERENCE_COOKIE = env.OSF_AUTH_COOKIE_NAME +OSF_AUTH_COOKIE_NAME = env.OSF_AUTH_COOKIE_NAME OSF_BASE_URL = env.OSF_BASE_URL.rstrip("/") OSF_API_BASE_URL = env.OSF_API_BASE_URL.rstrip("/") ALLOWED_RESOURCE_URI_PREFIXES = {OSF_BASE_URL} -# SESSION_COOKIE_DOMAIN = env.SESSION_COOKIE_DOMAIN SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_COOKIE_NAME = env.OSF_AUTH_COOKIE_NAME OSF_AUTH_COOKIE_SECRET = env.OSF_AUTH_COOKIE_SECRET @@ -76,12 +75,6 @@ "default": { "BACKEND": "django.core.cache.backends.redis.RedisCache", "LOCATION": REDIS_HOST, - # 'OPTIONS': { - # 'CLIENT_CLASS': 'django_redis.client.DefaultClient', - # 'CONNECTION_POOL_KWARGS': { - # 'max_connections': 100, - # }, - # }, }, } diff --git a/docker-compose.yml b/docker-compose.yml index 70bcfe26..2f2c5b4a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,6 @@ services: OSF_BASE_URL: "http://192.168.168.167:5000" OSF_API_BASE_URL: "http://192.168.168.167:8000" OSF_AUTH_COOKIE_NAME: osf - SESSION_COOKIE_DOMAIN: localhost AMQP_BROKER_URL: "amqp://guest:guest@rabbitmq:5672" GRAVYVALET_ENCRYPT_SECRET: "this is fine" ports: From 3dca7b826bbf499872fa25e5ce380fd1998cb608 Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Thu, 17 Apr 2025 10:55:42 -0400 Subject: [PATCH 025/100] fix linting --- addon_service/common/osf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addon_service/common/osf.py b/addon_service/common/osf.py index 2669c965..8650c982 100644 --- a/addon_service/common/osf.py +++ b/addon_service/common/osf.py @@ -71,6 +71,7 @@ async def get_osf_user_uri(request: django_http.HttpRequest) -> str | None: _response_content = await _response.json() return _iri_from_osfapi_resource(_response_content["data"]) + @async_to_sync async def has_osf_permission_on_resource( request: django_http.HttpRequest, From cbdba0b64c716bf771bf54344c7315515f1d8452 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 18 Apr 2025 12:00:07 +0300 Subject: [PATCH 026/100] fixed link config to have both web url and api url --- addon_imps/link/dataverse.py | 35 ++++++++++++------- addon_service/addon_imp/instantiation.py | 2 +- .../authorized_account/link/models.py | 2 ++ .../authorized_account/link/serializers.py | 1 - addon_service/common/serializer_fields.py | 4 +++ addon_service/external_service/link/models.py | 6 ++++ ...allinkservice_browser_base_url_and_more.py | 21 +++++++++++ .../resource_reference/serializers.py | 5 +-- addon_toolkit/interfaces/link.py | 1 + 9 files changed, 60 insertions(+), 17 deletions(-) create mode 100644 addon_service/migrations/0013_externallinkservice_browser_base_url_and_more.py diff --git a/addon_imps/link/dataverse.py b/addon_imps/link/dataverse.py index 77c1f78b..9f6ee80d 100644 --- a/addon_imps/link/dataverse.py +++ b/addon_imps/link/dataverse.py @@ -3,6 +3,7 @@ import asyncio import re from dataclasses import dataclass +from typing import Literal from django.core.exceptions import ValidationError @@ -27,13 +28,19 @@ class DataverseLinkImp(LinkAddonHttpRequestorImp): """ async def build_url_for_id(self, item_id: str) -> str: - match = DATASET_REGEX.match(item_id) - if match: - persistent_id = match["persistent_id"] - return f"{self.config.external_api_url}/dataset.xhtml?persistentId={persistent_id}" + if match := DATASET_REGEX.match(item_id): + entity_type = "dataset" elif match := FILE_REGEX.match(item_id): - persistent_id = match["persistent_id"] - return f"{self.config.external_api_url}/file.xhtml?persistentId={persistent_id}" + entity_type = "file" + else: + raise ValidationError(f"Invalid {item_id=}") + + persistent_id = match["persistent_id"] + + return self._make_url(entity_type, persistent_id) + + def _make_url(self, entity_type: Literal["file", "dataset"], persistent_id): + return f"{self.config.external_web_url}/{entity_type}.xhtml?persistentId={persistent_id}" async def get_external_account_id(self, _: dict[str, str]) -> str: try: @@ -101,7 +108,7 @@ async def list_child_items( ) elif match := DATASET_REGEX.match(item_id): items = await self._fetch_dataset_files( - dataset_id=match["id"], persistent_id=match["persistent_id"] + persistent_id=match["persistent_id"] ) return ItemSampleResult( items=items, @@ -169,12 +176,13 @@ def _parse_datafile(self, data: dict): if data.get("data"): data = data["data"] + doi = data["dataFile"]["persistentId"] return ItemResult( - item_id=f"file/{data['dataFile']['persistentId']}", + item_id=f"file/{doi}", item_name=data["label"], item_type=ItemType.RESOURCE, - item_link=f'{self.config.external_api_url}/file.xhtml?persistentId={data['dataFile']["persistentId"]}', - doi=data["dataFile"]["persistentId"], + item_link=self._make_url("file", doi), + doi=doi, ) def _parse_dataset_files(self, data: dict) -> list[ItemResult]: @@ -189,16 +197,17 @@ def _parse_dataset(self, data: dict) -> ItemResult: if data.get("data"): data = data["data"] try: + doi = data["datasetPersistentId"] return ItemResult( - item_id=f'dataset/{data["datasetPersistentId"]}', + item_id=f"dataset/{doi}", item_name=[ item for item in data["metadataBlocks"]["citation"]["fields"] if item["typeName"] == "title" ][0]["value"], item_type=ItemType.FOLDER, - item_link=f'{self.config.external_api_url}/dataset.xhtml?persistentId={data["datasetPersistentId"]}', - doi=data["datasetPersistentId"], + item_link=self._make_url("dataset", doi), + doi=doi, ) except (KeyError, IndexError) as e: raise ValueError(f"Invalid dataset response: {e=}") diff --git a/addon_service/addon_imp/instantiation.py b/addon_service/addon_imp/instantiation.py index 5b32500d..cad416fa 100644 --- a/addon_service/addon_imp/instantiation.py +++ b/addon_service/addon_imp/instantiation.py @@ -156,7 +156,7 @@ async def get_link_addon_instance( imp = imp_cls( network=GravyvaletHttpRequestor( client_session=await get_singleton_client_session(), - prefix_url=account.external_service.api_base_url, + prefix_url=config.external_api_url, account=account, ), config=config, diff --git a/addon_service/authorized_account/link/models.py b/addon_service/authorized_account/link/models.py index addc6ac9..6ac6880a 100644 --- a/addon_service/authorized_account/link/models.py +++ b/addon_service/authorized_account/link/models.py @@ -28,4 +28,6 @@ def configured_link_addons(self): def config(self) -> LinkConfig: return LinkConfig( external_api_url=self.api_base_url, + external_web_url=self.external_service.externallinkservice.browser_base_url + or self.api_base_url, ) diff --git a/addon_service/authorized_account/link/serializers.py b/addon_service/authorized_account/link/serializers.py index 8de8350f..cc2fe709 100644 --- a/addon_service/authorized_account/link/serializers.py +++ b/addon_service/authorized_account/link/serializers.py @@ -32,7 +32,6 @@ class AuthorizedLinkAccountSerializer(AuthorizedAccountSerializer): ) configured_link_addons = HyperlinkedRelatedField( many=True, - source="configured_addons", queryset=ConfiguredLinkAddon.objects.active(), related_link_view_name=view_names.related_view(RESOURCE_TYPE), required=False, diff --git a/addon_service/common/serializer_fields.py b/addon_service/common/serializer_fields.py index 1d63488d..83638d0a 100644 --- a/addon_service/common/serializer_fields.py +++ b/addon_service/common/serializer_fields.py @@ -64,6 +64,8 @@ def to_representation(self, value): data["type"] = "authorized-storage-accounts" elif hasattr(value, "authorizedcomputingaccount"): data["type"] = "authorized-computing-accounts" + elif hasattr(value, "authorizedlinkaccount"): + data["type"] = "authorized-link-accounts" elif isinstance(value, ConfiguredAddon): if hasattr(value, "configuredcitationaddon"): data["type"] = "configured-citation-addons" @@ -71,4 +73,6 @@ def to_representation(self, value): data["type"] = "configured-storage-addons" elif hasattr(value, "configuredcomputingaddon"): data["type"] = "configured-computing-addons" + elif hasattr(value, "configuredlinkaddon"): + data["type"] = "configured-link-addons" return data diff --git a/addon_service/external_service/link/models.py b/addon_service/external_service/link/models.py index dd2bfa92..8ebd148d 100644 --- a/addon_service/external_service/link/models.py +++ b/addon_service/external_service/link/models.py @@ -1,3 +1,4 @@ +from django.core.exceptions import ValidationError from django.db import models from addon_service.authorized_account.link.models import AuthorizedLinkAccount @@ -14,6 +15,7 @@ def validate_supported_features(value): class ExternalLinkService(ExternalService): + browser_base_url = models.URLField(blank=True, default="") int_supported_resource_types = models.BigIntegerField( validators=[validate_supported_features], null=True ) @@ -33,6 +35,10 @@ def supported_resource_types( def clean(self): super().clean() validate_link_imp_number(self.int_addon_imp) + if not self.api_base_url and not self.browser_base_url: + raise ValidationError( + "External Link Service should provide at least one of `browser_base_url` or `api_base_url`" + ) @property def authorized_link_accounts(self): diff --git a/addon_service/migrations/0013_externallinkservice_browser_base_url_and_more.py b/addon_service/migrations/0013_externallinkservice_browser_base_url_and_more.py new file mode 100644 index 00000000..fbe304cb --- /dev/null +++ b/addon_service/migrations/0013_externallinkservice_browser_base_url_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.7 on 2025-04-17 16:44 + +from django.db import ( + migrations, + models, +) + + +class Migration(migrations.Migration): + + dependencies = [ + ("addon_service", "0012_authorizedlinkaccount_configuredlinkaddon_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="externallinkservice", + name="browser_base_url", + field=models.URLField(blank=True, default=""), + ) + ] diff --git a/addon_service/resource_reference/serializers.py b/addon_service/resource_reference/serializers.py index eb1a79d3..48461d21 100644 --- a/addon_service/resource_reference/serializers.py +++ b/addon_service/resource_reference/serializers.py @@ -5,6 +5,7 @@ from addon_service.common import view_names from addon_service.configured_addon.citation.models import ConfiguredCitationAddon from addon_service.configured_addon.computing.models import ConfiguredComputingAddon +from addon_service.configured_addon.link.models import ConfiguredLinkAddon from addon_service.models import ( ConfiguredStorageAddon, ResourceReference, @@ -32,7 +33,7 @@ class ResourceReferenceSerializer(serializers.HyperlinkedModelSerializer): ) configured_link_addons = HyperlinkedRelatedField( many=True, - queryset=ConfiguredCitationAddon.objects.active(), + queryset=ConfiguredLinkAddon.objects.active(), related_link_view_name=view_names.related_view(RESOURCE_TYPE), ) configured_computing_addons = HyperlinkedRelatedField( @@ -49,7 +50,7 @@ class ResourceReferenceSerializer(serializers.HyperlinkedModelSerializer): "addon_service.serializers.ConfiguredCitationAddonSerializer" ), "configured_link_addons": ( - "addon_service.serializers.ConfiguredComputingAddonSerializer" + "addon_service.serializers.ConfiguredLinkAddonSerializer" ), "configured_computing_addons": ( "addon_service.serializers.ConfiguredComputingAddonSerializer" diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py index 25a23d37..cfab8570 100644 --- a/addon_toolkit/interfaces/link.py +++ b/addon_toolkit/interfaces/link.py @@ -77,6 +77,7 @@ class ItemResult: @dataclasses.dataclass(frozen=True) class LinkConfig: external_api_url: str + external_web_url: str @dataclasses.dataclass From 9c7a55c7174ba06d5423504e0832ae2f103f0491 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 22 Apr 2025 16:43:27 +0300 Subject: [PATCH 027/100] added PoC to get all verified links for given node --- .../configured_addon/link/serializers.py | 22 +++++++++++++++ addon_service/configured_addon/link/views.py | 27 ++++++++++++++++++- addon_service/configured_addon/views.py | 2 ++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index fea9efc3..839d34b0 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -2,6 +2,7 @@ CharField, URLField, ) +from rest_framework_json_api import serializers from rest_framework_json_api.relations import ResourceRelatedField from rest_framework_json_api.utils import get_resource_type_from_model @@ -78,3 +79,24 @@ class Meta: "resource_type", "target_id", ] + + +class VerifiedLink(serializers.Serializer): + """Serialize ConfiguredStorageAddon information required by WaterButler. + + The returned data should share a shape with the existing `serialize_waterbutler_credentials` + and `serialize_waterbutler_settings` functions used by the OSF-based Addons. + + NB: The Boa addon needs credentials from GV, and this seems like a good short-term + place to hang it. Boa doesn't actually connect with WB, so this will probably be + refactored in the future. But for now, any configured_storage_addon could actually + be a configured_computed_addon. + """ + + class JSONAPIMeta: + resource_name = "verified-link" + + target_url = URLField(read_only=True) + target_id = CharField() + resource_type = EnumNameChoiceField(enum_cls=SupportedResourceTypes) + service_name = CharField(source="external_service.external_service_name") diff --git a/addon_service/configured_addon/link/views.py b/addon_service/configured_addon/link/views.py index ecadab51..fc64394c 100644 --- a/addon_service/configured_addon/link/views.py +++ b/addon_service/configured_addon/link/views.py @@ -1,7 +1,16 @@ +from http import HTTPMethod + +from rest_framework.decorators import action +from rest_framework.response import Response + from addon_service.configured_addon.views import ConfiguredAddonViewSet +from app.settings import ALLOWED_RESOURCE_URI_PREFIXES from .models import ConfiguredLinkAddon -from .serializers import ConfiguredLinkAddonSerializer +from .serializers import ( + ConfiguredLinkAddonSerializer, + VerifiedLink, +) class ConfiguredLinkAddonViewSet(ConfiguredAddonViewSet): @@ -12,3 +21,19 @@ class ConfiguredLinkAddonViewSet(ConfiguredAddonViewSet): "authorized_resource", ) serializer_class = ConfiguredLinkAddonSerializer + + @action( + detail=True, + methods=[HTTPMethod.GET], + url_name="verified-link", + url_path="verified-link", + ) + def verified_link(self, request, pk=None): + addons: ConfiguredLinkAddon = ConfiguredLinkAddon.objects.filter( + authorized_resource__resource_uri__in=[ + f"{prefix}/{pk}" for prefix in ALLOWED_RESOURCE_URI_PREFIXES + ], + ).select_related("base_account__external_service") + self.resource_name = "verified-link" + + return Response(VerifiedLink(addons, many=True).data) diff --git a/addon_service/configured_addon/views.py b/addon_service/configured_addon/views.py index fe1189c3..3b1cfaec 100644 --- a/addon_service/configured_addon/views.py +++ b/addon_service/configured_addon/views.py @@ -21,6 +21,8 @@ def get_permissions(self): return [IsAuthenticated(), SessionUserMayConnectAddon()] case "get_wb_credentials": return [IsValidHMACSignedRequest()] + case "verified_link": + return [IsAuthenticated()] case None: return super().get_permissions() case _: From 8bff195c58aea0ab1f64728152aa8cc0ccc8e102 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 22 Apr 2025 16:45:20 +0300 Subject: [PATCH 028/100] fixed permissions --- addon_service/configured_addon/views.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/addon_service/configured_addon/views.py b/addon_service/configured_addon/views.py index 3b1cfaec..da331eea 100644 --- a/addon_service/configured_addon/views.py +++ b/addon_service/configured_addon/views.py @@ -19,10 +19,8 @@ def get_permissions(self): return [IsAuthenticated(), SessionUserIsOwnerOrResourceAdmin()] case "create": return [IsAuthenticated(), SessionUserMayConnectAddon()] - case "get_wb_credentials": + case "get_wb_credentials" | "verified_link": return [IsValidHMACSignedRequest()] - case "verified_link": - return [IsAuthenticated()] case None: return super().get_permissions() case _: From 44815121890bf6912a2035d35f0988d7ab2220dd Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Wed, 23 Apr 2025 00:26:48 -0400 Subject: [PATCH 029/100] fix test --- addon_service/common/osf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon_service/common/osf.py b/addon_service/common/osf.py index 8650c982..aa77f610 100644 --- a/addon_service/common/osf.py +++ b/addon_service/common/osf.py @@ -56,7 +56,7 @@ async def get_osf_user_uri(request: django_http.HttpRequest) -> str | None: pass # the only acceptable hmac-related error is not using hmac at all # if not hmac, check to see if it's in shared session - _user_referernce_uri = request.session.get["user_reference_uri"] + _user_referernce_uri = request.session.get("user_reference_uri") if _user_referernce_uri: return _user_referernce_uri From ef532989620f8a28d53142482ff1cf46c52f081e Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Thu, 24 Apr 2025 13:25:03 +0300 Subject: [PATCH 030/100] fixed comments --- addon_service/configured_addon/link/serializers.py | 11 +---------- addon_service/configured_addon/link/views.py | 6 +++--- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index 839d34b0..ccb84cc1 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -82,16 +82,7 @@ class Meta: class VerifiedLink(serializers.Serializer): - """Serialize ConfiguredStorageAddon information required by WaterButler. - - The returned data should share a shape with the existing `serialize_waterbutler_credentials` - and `serialize_waterbutler_settings` functions used by the OSF-based Addons. - - NB: The Boa addon needs credentials from GV, and this seems like a good short-term - place to hang it. Boa doesn't actually connect with WB, so this will probably be - refactored in the future. But for now, any configured_storage_addon could actually - be a configured_computed_addon. - """ + """Serialize VerifiedLinks information required by OSF.""" class JSONAPIMeta: resource_name = "verified-link" diff --git a/addon_service/configured_addon/link/views.py b/addon_service/configured_addon/link/views.py index fc64394c..b5825479 100644 --- a/addon_service/configured_addon/link/views.py +++ b/addon_service/configured_addon/link/views.py @@ -25,10 +25,10 @@ class ConfiguredLinkAddonViewSet(ConfiguredAddonViewSet): @action( detail=True, methods=[HTTPMethod.GET], - url_name="verified-link", - url_path="verified-link", + url_name="verified-links", + url_path="verified-links", ) - def verified_link(self, request, pk=None): + def get_verified_links(self, request, pk=None): addons: ConfiguredLinkAddon = ConfiguredLinkAddon.objects.filter( authorized_resource__resource_uri__in=[ f"{prefix}/{pk}" for prefix in ALLOWED_RESOURCE_URI_PREFIXES From 0231a3690b936db20ca3087e2f389d92b7817962 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Thu, 24 Apr 2025 13:29:26 +0300 Subject: [PATCH 031/100] fixed comment for verified link class --- addon_service/configured_addon/link/serializers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index ccb84cc1..7ffb4691 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -82,7 +82,11 @@ class Meta: class VerifiedLink(serializers.Serializer): - """Serialize VerifiedLinks information required by OSF.""" + """Serialize ConfiguredLinkAddon information required by OSF. + + The information is shaped for osf to be able to update datacite and share metadata + with minimal performance footprint + """ class JSONAPIMeta: resource_name = "verified-link" From e5ff0d16e3eb714c09ba3cb521c2b379627d0f5a Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Thu, 24 Apr 2025 15:51:29 +0300 Subject: [PATCH 032/100] fixed permissions --- addon_service/configured_addon/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon_service/configured_addon/views.py b/addon_service/configured_addon/views.py index da331eea..eb0bf0c4 100644 --- a/addon_service/configured_addon/views.py +++ b/addon_service/configured_addon/views.py @@ -19,7 +19,7 @@ def get_permissions(self): return [IsAuthenticated(), SessionUserIsOwnerOrResourceAdmin()] case "create": return [IsAuthenticated(), SessionUserMayConnectAddon()] - case "get_wb_credentials" | "verified_link": + case "get_wb_credentials" | "get_verified_links": return [IsValidHMACSignedRequest()] case None: return super().get_permissions() From 7d5f81b6704161492961babb712ea5140edfb67d Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Thu, 24 Apr 2025 16:16:28 +0300 Subject: [PATCH 033/100] implemented sending celery task when links change --- addon_service/common/regex.py | 4 ++++ addon_service/configured_addon/link/models.py | 9 ++++++++- addon_service/configured_addon/models.py | 4 ++-- addon_service/resource_reference/models.py | 8 ++++++++ addon_service/user_reference/models.py | 5 +++++ app/celery.py | 1 + 6 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 addon_service/common/regex.py diff --git a/addon_service/common/regex.py b/addon_service/common/regex.py new file mode 100644 index 00000000..2dfe2127 --- /dev/null +++ b/addon_service/common/regex.py @@ -0,0 +1,4 @@ +import re + + +uri_regex = re.compile(r"http://[^/]+/(?P\w{5})/?") diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index 0b5a411a..2cbc0f51 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -7,6 +7,7 @@ LinkConfig, SupportedResourceTypes, ) +from app.celery import app def is_supported_resource_type(resource_type: int): @@ -19,7 +20,6 @@ def is_supported_resource_type(resource_type: int): class ConfiguredLinkAddon(ConfiguredAddon): - target_id = models.CharField() int_resource_type = models.BigIntegerField(validators=[is_supported_resource_type]) @@ -31,6 +31,13 @@ def resource_type(self) -> str: def resource_type(self, value) -> None: self.int_resource_type = value.value + def save(self, *args, full_clean=True, **kwargs): + super().save(*args, full_clean=full_clean, **kwargs) + app.send_task( + "website.identifiers.tasks.task__update_doi_metadata_with_verified_links", + kwargs={"target_guid": self.authorized_resource.guid}, + ) + def target_url(self): if self.target_id: from addon_service.addon_imp.instantiation import ( diff --git a/addon_service/configured_addon/models.py b/addon_service/configured_addon/models.py index ce91544c..be3727e5 100644 --- a/addon_service/configured_addon/models.py +++ b/addon_service/configured_addon/models.py @@ -96,8 +96,8 @@ def save(self, *args, full_clean=True, **kwargs): app.send_task( "osf.tasks.log_gv_addon", kwargs={ - "node_url": self.resource_uri, - "user_url": self.owner_uri, + "node_guid": self.authorized_resource.guid, + "user_guid": self.account_owner.guid, "addon": self.display_name, "action": "addon_added", }, diff --git a/addon_service/resource_reference/models.py b/addon_service/resource_reference/models.py index b83021f8..5e447bd2 100644 --- a/addon_service/resource_reference/models.py +++ b/addon_service/resource_reference/models.py @@ -2,6 +2,7 @@ from django.db.models.functions import Lower from addon_service.common.base_model import AddonsServiceBaseModel +from addon_service.common.regex import uri_regex from addon_service.configured_addon.citation.models import ConfiguredCitationAddon from addon_service.configured_addon.computing.models import ConfiguredComputingAddon from addon_service.configured_addon.link.models import ConfiguredLinkAddon @@ -53,6 +54,13 @@ def configured_computing_addons(self): "base_account__account_owner", ) + @property + def guid(self) -> str | None: + match = uri_regex.match(self.resource_uri) + if match: + return match["id"] + return None + class Meta: verbose_name = "Resource Reference" verbose_name_plural = "Resource References" diff --git a/addon_service/user_reference/models.py b/addon_service/user_reference/models.py index 3b2b39c4..ad189e98 100644 --- a/addon_service/user_reference/models.py +++ b/addon_service/user_reference/models.py @@ -6,6 +6,7 @@ from addon_service.authorized_account.link.models import AuthorizedLinkAccount from addon_service.authorized_account.storage.models import AuthorizedStorageAccount from addon_service.common.base_model import AddonsServiceBaseModel +from addon_service.common.regex import uri_regex from addon_service.configured_addon.computing.models import ConfiguredComputingAddon from addon_service.configured_addon.storage.models import ConfiguredStorageAddon from addon_service.resource_reference.models import ResourceReference @@ -74,6 +75,10 @@ class JSONAPIMeta: def owner_uri(self) -> str: return self.user_uri + @property + def guid(self) -> str: + return uri_regex.match(self.user_uri) + def deactivate(self): self.deactivated = timezone.now() self.save() diff --git a/app/celery.py b/app/celery.py index c51c63fe..c972607d 100644 --- a/app/celery.py +++ b/app/celery.py @@ -51,6 +51,7 @@ def queue_name(self) -> str: "queue": gv_chill_queue }, "osf.*": {"queue": osf_high_queue}, + "website.*": {"queue": osf_high_queue}, }, include=[ "addon_service.tasks", From e17fa44860ab00b22ea62a7cc0ad5375b9a56e71 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Thu, 24 Apr 2025 16:18:28 +0300 Subject: [PATCH 034/100] fixed user guid --- addon_service/user_reference/models.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addon_service/user_reference/models.py b/addon_service/user_reference/models.py index ad189e98..ef004e56 100644 --- a/addon_service/user_reference/models.py +++ b/addon_service/user_reference/models.py @@ -76,8 +76,11 @@ def owner_uri(self) -> str: return self.user_uri @property - def guid(self) -> str: - return uri_regex.match(self.user_uri) + def guid(self) -> str | None: + match = uri_regex.match(self.user_uri) + if match: + return match["id"] + return None def deactivate(self): self.deactivated = timezone.now() From bd48926a0ca96ebbb6b6acf53e003a9669200bfc Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 25 Apr 2025 17:13:04 +0300 Subject: [PATCH 035/100] fixed Resource types to be consistent with datacite --- addon_toolkit/interfaces/link.py | 69 +++++++++++++++++--------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py index cfab8570..f55e48a3 100644 --- a/addon_toolkit/interfaces/link.py +++ b/addon_toolkit/interfaces/link.py @@ -30,38 +30,43 @@ class ItemType(enum.StrEnum): class SupportedResourceTypes(enum.Flag): - AUDIOVISUAL = enum.auto() - AWARD = enum.auto() - BOOK = enum.auto() - BOOK_CHAPTER = enum.auto() - COLLECTION = enum.auto() - COMPUTATIONAL_NOTEBOOK = enum.auto() - CONFERENCE_PAPER = enum.auto() - CONFERENCE_PROCEEDING = enum.auto() - DATA_PAPER = enum.auto() - DATASET = enum.auto() - DISSERTATION = enum.auto() - EVENT = enum.auto() - IMAGE = enum.auto() - INSTRUMENT = enum.auto() - INTERACTIVE_RESOURCE = enum.auto() - JOURNAL = enum.auto() - JOURNAL_ARTICLE = enum.auto() - MODEL = enum.auto() - OUTPUT_MANAGEMENT_PLAN = enum.auto() - PEER_REVIEW = enum.auto() - PHYSICAL_OBJECT = enum.auto() - PREPRINT = enum.auto() - PROJECT = enum.auto() - REPORT = enum.auto() - SERVICE = enum.auto() - SOFTWARE = enum.auto() - SOUND = enum.auto() - STANDARD = enum.auto() - STUDY_REGISTRATION = enum.auto() - TEXT = enum.auto() - WORKFLOW = enum.auto() - OTHER = enum.auto() + """ + Even though this is not conventional for python enums to have upper camel case, we opt for it to follow + https://datacite-metadata-schema.readthedocs.io/en/4.6/appendices/appendix-1/resourceTypeGeneral/ + """ + + Audiovisual = enum.auto() + Award = enum.auto() + Book = enum.auto() + BookChapter = enum.auto() + Collection = enum.auto() + ComputationalNotebook = enum.auto() + ConferencePaper = enum.auto() + ConferenceProceeding = enum.auto() + DataPaper = enum.auto() + Dataset = enum.auto() + Dissertation = enum.auto() + Event = enum.auto() + Image = enum.auto() + Instrument = enum.auto() + InteractiveResource = enum.auto() + Journal = enum.auto() + JournalArticle = enum.auto() + Model = enum.auto() + OutputManagement_plan = enum.auto() + PeerReview = enum.auto() + PhysicalObject = enum.auto() + Preprint = enum.auto() + Project = enum.auto() + Report = enum.auto() + Service = enum.auto() + Software = enum.auto() + Sound = enum.auto() + Standard = enum.auto() + StudyRegistration = enum.auto() + Text = enum.auto() + Workflow = enum.auto() + Other = enum.auto() @dataclasses.dataclass From 37db09c78842a3e3cf3e988d377f116c8b84fecb Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 25 Apr 2025 17:39:05 +0300 Subject: [PATCH 036/100] fixed access token auth for user-references --- addon_service/authentication.py | 1 + addon_service/common/permissions.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/addon_service/authentication.py b/addon_service/authentication.py index e9dbe28d..7d1d19ac 100644 --- a/addon_service/authentication.py +++ b/addon_service/authentication.py @@ -12,6 +12,7 @@ def authenticate(self, request: DrfRequest): _user_uri = osf.get_osf_user_uri(request) if _user_uri: UserReference.objects.get_or_create(user_uri=_user_uri) + request.user_uri = _user_uri return True, None return None # unauthenticated diff --git a/addon_service/common/permissions.py b/addon_service/common/permissions.py index 6bdf7e40..5bb14e3c 100644 --- a/addon_service/common/permissions.py +++ b/addon_service/common/permissions.py @@ -19,7 +19,7 @@ class SessionUserIsOwner(permissions.BasePermission): """for object permissions on objects with `owner_uri`""" def has_object_permission(self, request, view, obj): - session_user_uri = request.session.get("user_reference_uri") + session_user_uri = request.session.get("user_reference_uri", request.user_uri) if session_user_uri: return session_user_uri == obj.owner_uri return False From 06df870585ac98511961c7b0c9f56ac0c351f27c Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 25 Apr 2025 17:45:49 +0300 Subject: [PATCH 037/100] fixed tests --- addon_service/common/permissions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addon_service/common/permissions.py b/addon_service/common/permissions.py index 5bb14e3c..69b8e9d4 100644 --- a/addon_service/common/permissions.py +++ b/addon_service/common/permissions.py @@ -19,7 +19,9 @@ class SessionUserIsOwner(permissions.BasePermission): """for object permissions on objects with `owner_uri`""" def has_object_permission(self, request, view, obj): - session_user_uri = request.session.get("user_reference_uri", request.user_uri) + session_user_uri = request.session.get( + "user_reference_uri", getattr(request, "user_uri", None) + ) if session_user_uri: return session_user_uri == obj.owner_uri return False From 9cf0e9546cb82c9af15ab90cf22ef56049c489af Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 25 Apr 2025 18:08:27 +0300 Subject: [PATCH 038/100] fixed PAT for IsAuthenticated checks --- addon_service/common/permissions.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/addon_service/common/permissions.py b/addon_service/common/permissions.py index 69b8e9d4..3a0a3895 100644 --- a/addon_service/common/permissions.py +++ b/addon_service/common/permissions.py @@ -12,7 +12,12 @@ class IsAuthenticated(permissions.BasePermission): """allow any logged-in user""" def has_permission(self, request, view): - return request.session.get("user_reference_uri") is not None + return ( + request.session.get( + "user_reference_uri", getattr(request, "user_uri", None) + ) + is not None + ) class SessionUserIsOwner(permissions.BasePermission): From 2a44bb2022ca151776ef8838d2da2cd883d2eaaf Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 28 Apr 2025 18:18:19 +0300 Subject: [PATCH 039/100] final fix for PAT --- .../addon_operation_invocation/serializers.py | 6 ++---- .../authorized_account/serializers.py | 3 ++- addon_service/common/get_user_uri.py | 8 ++++++++ addon_service/common/osf.py | 3 ++- addon_service/common/permissions.py | 20 +++++++------------ addon_service/configured_addon/serializers.py | 5 ++--- 6 files changed, 23 insertions(+), 22 deletions(-) create mode 100644 addon_service/common/get_user_uri.py diff --git a/addon_service/addon_operation_invocation/serializers.py b/addon_service/addon_operation_invocation/serializers.py index 80be67af..d6f97d4e 100644 --- a/addon_service/addon_operation_invocation/serializers.py +++ b/addon_service/addon_operation_invocation/serializers.py @@ -9,6 +9,7 @@ ) from addon_service.common import view_names from addon_service.common.enum_serializers import EnumNameChoiceField +from addon_service.common.get_user_uri import get_user_uri from addon_service.common.invocation_status import InvocationStatus from addon_service.common.serializer_fields import ( CustomPolymorphicResourceRelatedField, @@ -113,10 +114,7 @@ def create(self, validated_data): _imp_cls = _thru_account.imp_cls _operation = _imp_cls.get_operation_declaration(_operation_name) _request = self.context["request"] - _user_uri = ( - _request.session.get("user_reference_uri") - or f"{settings.OSF_BASE_URL}/anonymous" - ) + _user_uri = get_user_uri(_request) or f"{settings.OSF_BASE_URL}/anonymous" _user, _ = UserReference.objects.get_or_create(user_uri=_user_uri) return AddonOperationInvocation( operation=AddonOperationModel(_imp_cls.ADDON_INTERFACE, _operation), diff --git a/addon_service/authorized_account/serializers.py b/addon_service/authorized_account/serializers.py index 6367a6d4..40b85c59 100644 --- a/addon_service/authorized_account/serializers.py +++ b/addon_service/authorized_account/serializers.py @@ -8,6 +8,7 @@ from addon_service.authorized_account.models import AuthorizedAccount from addon_service.common.credentials_formats import CredentialsFormats +from addon_service.common.get_user_uri import get_user_uri from addon_service.external_service.models import ExternalService from addon_service.osf_models.fields import encrypt_string from addon_service.serializer_fields import ( @@ -72,7 +73,7 @@ def create_authorized_account( api_base_url: str = "", **kwargs, ) -> AuthorizedAccount: - session_user_uri = self.context["request"].session.get("user_reference_uri") + session_user_uri = get_user_uri(self.context["request"]) account_owner, _ = UserReference.objects.get_or_create( user_uri=session_user_uri ) diff --git a/addon_service/common/get_user_uri.py b/addon_service/common/get_user_uri.py new file mode 100644 index 00000000..7c35add2 --- /dev/null +++ b/addon_service/common/get_user_uri.py @@ -0,0 +1,8 @@ +def get_user_uri(request) -> str | None: + """ + Helper function which returns user's uri + It supports both session auth and PAT, which must not store any info in session (because they may be revoked later) + """ + return request.session.get("user_reference_uri") or getattr( + request, "user_uri", None + ) diff --git a/addon_service/common/osf.py b/addon_service/common/osf.py index aa77f610..4104bff8 100644 --- a/addon_service/common/osf.py +++ b/addon_service/common/osf.py @@ -11,6 +11,7 @@ from addon_service.common import hmac as hmac_utils from addon_service.common.aiohttp_session import get_singleton_client_session +from addon_service.common.get_user_uri import get_user_uri from addon_toolkit import AddonCapabilities @@ -56,7 +57,7 @@ async def get_osf_user_uri(request: django_http.HttpRequest) -> str | None: pass # the only acceptable hmac-related error is not using hmac at all # if not hmac, check to see if it's in shared session - _user_referernce_uri = request.session.get("user_reference_uri") + _user_referernce_uri = get_user_uri(request) if _user_referernce_uri: return _user_referernce_uri diff --git a/addon_service/common/permissions.py b/addon_service/common/permissions.py index 3a0a3895..f9b4294c 100644 --- a/addon_service/common/permissions.py +++ b/addon_service/common/permissions.py @@ -6,27 +6,21 @@ from addon_service.common import hmac as hmac_utils from addon_service.common import osf +from addon_service.common.get_user_uri import get_user_uri class IsAuthenticated(permissions.BasePermission): """allow any logged-in user""" def has_permission(self, request, view): - return ( - request.session.get( - "user_reference_uri", getattr(request, "user_uri", None) - ) - is not None - ) + return get_user_uri(request) is not None class SessionUserIsOwner(permissions.BasePermission): """for object permissions on objects with `owner_uri`""" def has_object_permission(self, request, view, obj): - session_user_uri = request.session.get( - "user_reference_uri", getattr(request, "user_uri", None) - ) + session_user_uri = get_user_uri(request) if session_user_uri: return session_user_uri == obj.owner_uri return False @@ -38,7 +32,7 @@ class SessionUserIsOwnerOrResourceAdmin(permissions.BasePermission): message = "Permission denied, to perform this action you should be the one who created this addon or project admin" def has_object_permission(self, request, view, obj): - _user_uri = request.session.get("user_reference_uri") + _user_uri = get_user_uri(request) return (_user_uri == obj.owner_uri) or osf.has_osf_permission_on_resource( request, obj.resource_uri, @@ -61,7 +55,7 @@ class SessionUserMayConnectAddon(permissions.BasePermission): """for object permissions on objects with `owner_uri` and `resource_uri`""" def has_object_permission(self, request, view, obj): - _user_uri = request.session.get("user_reference_uri") + _user_uri = get_user_uri(request) return ( # must be account owner (_user_uri == obj.owner_uri) @@ -78,7 +72,7 @@ class SessionUserMayAccessInvocation(permissions.BasePermission): """for object permissions on `addon_service.models.AddonOperationInvocation`""" def has_object_permission(self, request, view, obj): - _user_uri = request.session.get("user_reference_uri") + _user_uri = get_user_uri(request) return bool( # must be the invoker: (_user_uri == obj.by_user.user_uri) @@ -97,7 +91,7 @@ class SessionUserMayPerformInvocation(permissions.BasePermission): """for object permissions on `addon_service.models.AddonOperationInvocation`""" def has_object_permission(self, request, view, obj): - _user_uri = request.session.get("user_reference_uri") + _user_uri = get_user_uri(request) _thru_addon = obj.thru_addon _thru_account = obj.thru_account if _thru_addon is None: diff --git a/addon_service/configured_addon/serializers.py b/addon_service/configured_addon/serializers.py index ce6d92ad..ded2328e 100644 --- a/addon_service/configured_addon/serializers.py +++ b/addon_service/configured_addon/serializers.py @@ -2,6 +2,7 @@ from rest_framework_json_api import serializers from addon_service.common.enum_serializers import EnumNameMultipleChoiceField +from addon_service.common.get_user_uri import get_user_uri from addon_service.configured_addon.models import ConfiguredAddon from addon_toolkit import AddonCapabilities @@ -33,9 +34,7 @@ def __init__(self, *args, **kwargs): current_user_is_owner = serializers.SerializerMethodField() def get_current_user_is_owner(self, configured_addon: ConfiguredAddon): - return configured_addon.owner_uri == self.context["request"].session.get( - "user_reference_uri" - ) + return configured_addon.owner_uri == get_user_uri(self.context["request"]) def create(self, validated_data): validated_data = self.fix_dotted_base_account(validated_data) From 2f2ef0e2960089667d035b51f2d6d767ee9cdc0f Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 29 Apr 2025 13:03:36 -0400 Subject: [PATCH 040/100] Re-run lock and update poetry.lock --- poetry.lock | 81 ++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/poetry.lock b/poetry.lock index 64548921..e4a9d38d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -113,7 +113,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -173,12 +173,12 @@ files = [ ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""] [[package]] name = "autobahn" @@ -199,13 +199,13 @@ setuptools = "*" txaio = ">=21.2.1" [package.extras] -all = ["PyGObject (>=3.40.0)", "argon2-cffi (>=20.1.0)", "attrs (>=20.3.0)", "base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "cffi (>=1.14.5)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "flatbuffers (>=22.12.6)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "msgpack (>=1.0.2)", "passlib (>=1.7.4)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "py-ubjson (>=0.16.1)", "pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "python-snappy (>=0.6.0)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "rlp (>=2.0.1)", "service-identity (>=18.1.0)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "twisted (>=24.3.0)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)", "zope.interface (>=5.2.0)"] +all = ["PyGObject (>=3.40.0)", "argon2-cffi (>=20.1.0)", "attrs (>=20.3.0)", "base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "cffi (>=1.14.5)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "flatbuffers (>=22.12.6)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "msgpack (>=1.0.2) ; platform_python_implementation == \"CPython\"", "passlib (>=1.7.4)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "py-ubjson (>=0.16.1)", "pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "python-snappy (>=0.6.0)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "rlp (>=2.0.1)", "service-identity (>=18.1.0)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "twisted (>=24.3.0)", "u-msgpack-python (>=2.1) ; platform_python_implementation != \"CPython\"", "ujson (>=4.0.2) ; platform_python_implementation == \"CPython\"", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)", "zope.interface (>=5.2.0)"] compress = ["python-snappy (>=0.6.0)"] -dev = ["backports.tempfile (>=1.0)", "build (>=1.2.1)", "bumpversion (>=0.5.3)", "codecov (>=2.0.15)", "flake8 (<5)", "humanize (>=0.5.1)", "mypy (>=0.610)", "passlib", "pep8-naming (>=0.3.3)", "pip (>=9.0.1)", "pyenchant (>=1.6.6)", "pyflakes (>=1.0.0)", "pyinstaller (>=4.2)", "pylint (>=1.9.2)", "pytest (>=3.4.2)", "pytest-aiohttp", "pytest-asyncio (>=0.14.0)", "pytest-runner (>=2.11.1)", "pyyaml (>=4.2b4)", "qualname", "sphinx (>=1.7.1)", "sphinx-autoapi (>=1.7.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-images (>=0.9.1)", "tox (>=4.2.8)", "tox-gh-actions (>=2.2.0)", "twine (>=3.3.0)", "twisted (>=22.10.0)", "txaio (>=20.4.1)", "watchdog (>=0.8.3)", "wheel (>=0.36.2)", "yapf (==0.29.0)"] +dev = ["backports.tempfile (>=1.0)", "build (>=1.2.1)", "bumpversion (>=0.5.3)", "codecov (>=2.0.15)", "flake8 (<5)", "humanize (>=0.5.1)", "mypy (>=0.610) ; python_version >= \"3.4\" and platform_python_implementation != \"PyPy\"", "passlib", "pep8-naming (>=0.3.3)", "pip (>=9.0.1)", "pyenchant (>=1.6.6)", "pyflakes (>=1.0.0)", "pyinstaller (>=4.2)", "pylint (>=1.9.2)", "pytest (>=3.4.2)", "pytest-aiohttp", "pytest-asyncio (>=0.14.0)", "pytest-runner (>=2.11.1)", "pyyaml (>=4.2b4)", "qualname", "sphinx (>=1.7.1)", "sphinx-autoapi (>=1.7.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-images (>=0.9.1)", "tox (>=4.2.8)", "tox-gh-actions (>=2.2.0)", "twine (>=3.3.0)", "twisted (>=22.10.0)", "txaio (>=20.4.1)", "watchdog (>=0.8.3)", "wheel (>=0.36.2)", "yapf (==0.29.0)"] encryption = ["pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "service-identity (>=18.1.0)"] nvx = ["cffi (>=1.14.5)"] scram = ["argon2-cffi (>=20.1.0)", "cffi (>=1.14.5)", "passlib (>=1.7.4)"] -serialization = ["cbor2 (>=5.2.0)", "flatbuffers (>=22.12.6)", "msgpack (>=1.0.2)", "py-ubjson (>=0.16.1)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)"] +serialization = ["cbor2 (>=5.2.0)", "flatbuffers (>=22.12.6)", "msgpack (>=1.0.2) ; platform_python_implementation == \"CPython\"", "py-ubjson (>=0.16.1)", "u-msgpack-python (>=2.1) ; platform_python_implementation != \"CPython\"", "ujson (>=4.0.2) ; platform_python_implementation == \"CPython\""] twisted = ["attrs (>=20.3.0)", "twisted (>=24.3.0)", "zope.interface (>=5.2.0)"] ui = ["PyGObject (>=3.40.0)"] xbr = ["base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "rlp (>=2.0.1)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)"] @@ -330,33 +330,33 @@ vine = ">=5.1.0,<6.0" arangodb = ["pyArango (>=2.0.2)"] auth = ["cryptography (==44.0.2)"] azureblockblob = ["azure-identity (>=1.19.0)", "azure-storage-blob (>=12.15.0)"] -brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] +brotli = ["brotli (>=1.0.0) ; platform_python_implementation == \"CPython\"", "brotlipy (>=0.7.0) ; platform_python_implementation == \"PyPy\""] cassandra = ["cassandra-driver (>=3.25.0,<4)"] consul = ["python-consul2 (==0.1.5)"] cosmosdbsql = ["pydocumentdb (==2.3.5)"] -couchbase = ["couchbase (>=3.0.0)"] +couchbase = ["couchbase (>=3.0.0) ; platform_python_implementation != \"PyPy\" and (platform_system != \"Windows\" or python_version < \"3.10\")"] couchdb = ["pycouchdb (==1.16.0)"] django = ["Django (>=2.2.28)"] dynamodb = ["boto3 (>=1.26.143)"] elasticsearch = ["elastic-transport (<=8.17.1)", "elasticsearch (<=8.17.2)"] -eventlet = ["eventlet (>=0.32.0)"] +eventlet = ["eventlet (>=0.32.0) ; python_version < \"3.10\""] gcs = ["google-cloud-firestore (==2.20.1)", "google-cloud-storage (>=2.10.0)", "grpcio (==1.67.0)"] gevent = ["gevent (>=1.5.0)"] -librabbitmq = ["librabbitmq (>=2.0.0)"] -memcache = ["pylibmc (==1.6.3)"] +librabbitmq = ["librabbitmq (>=2.0.0) ; python_version < \"3.11\""] +memcache = ["pylibmc (==1.6.3) ; platform_system != \"Windows\""] mongodb = ["pymongo (==4.10.1)"] msgpack = ["msgpack (==1.1.0)"] pydantic = ["pydantic (>=2.4)"] pymemcache = ["python-memcached (>=1.61)"] -pyro = ["pyro4 (==4.82)"] +pyro = ["pyro4 (==4.82) ; python_version < \"3.11\""] pytest = ["pytest-celery[all] (>=1.2.0,<1.3.0)"] redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] s3 = ["boto3 (>=1.26.143)"] slmq = ["softlayer_messaging (>=1.0.3)"] -solar = ["ephem (==4.2)"] +solar = ["ephem (==4.2) ; platform_python_implementation != \"PyPy\""] sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.4)", "urllib3 (>=1.26.16)"] -tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] +tblib = ["tblib (>=1.3.0) ; python_version < \"3.8.0\"", "tblib (>=1.5.0) ; python_version >= \"3.8.0\""] yaml = ["PyYAML (>=3.10)"] zookeeper = ["kazoo (>=1.3.1)"] zstd = ["zstandard (==0.23.0)"] @@ -718,10 +718,10 @@ files = [ cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version >= \"3.8\""] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""] +pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.1)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] @@ -950,7 +950,7 @@ files = [ [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8)"] +typing = ["typing-extensions (>=4.8) ; python_version < \"3.11\""] [[package]] name = "flake8" @@ -1257,7 +1257,7 @@ azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6. confluentkafka = ["confluent-kafka (>=2.2.0)"] consul = ["python-consul2 (==0.1.5)"] gcpubsub = ["google-cloud-monitoring (>=2.16.0)", "google-cloud-pubsub (>=2.18.4)", "grpcio (==1.67.0)", "protobuf (==4.25.5)"] -librabbitmq = ["librabbitmq (>=2.0.0)"] +librabbitmq = ["librabbitmq (>=2.0.0) ; python_version < \"3.11\""] mongodb = ["pymongo (>=4.1.1)"] msgpack = ["msgpack (==1.1.0)"] pyro = ["pyro4 (==4.82)"] @@ -1703,8 +1703,8 @@ typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.2.6)"] -c = ["psycopg-c (==3.2.6)"] +binary = ["psycopg-binary (==3.2.6) ; implementation_name != \"pypy\""] +c = ["psycopg-c (==3.2.6) ; implementation_name != \"pypy\""] dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] @@ -2080,7 +2080,7 @@ requests = ">=2.30.0,<3.0" urllib3 = ">=1.25.10,<3.0" [package.extras] -tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli", "tomli-w", "types-PyYAML", "types-requests"] +tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli ; python_version < \"3.11\"", "tomli-w", "types-PyYAML", "types-requests"] [[package]] name = "rpds-py" @@ -2302,9 +2302,9 @@ files = [ ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6) ; python_version < \"3.10\"", "importlib-resources (>=5.10.2) ; python_version < \"3.9\"", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-ruff (<0.4) ; platform_system == \"Windows\"", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "pytest-ruff (>=0.3.2) ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -2381,19 +2381,19 @@ typing-extensions = ">=4.2.0" zope-interface = ">=5" [package.extras] -all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"] dev = ["coverage (>=7.5,<8.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "python-subunit (>=1.4,<2.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)"] dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"] -gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"] -macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] -mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"] -osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] -serial = ["pyserial (>=3.0)", "pywin32 (!=226)"] +macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"] +osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +serial = ["pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\""] test = ["cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"] tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"] -windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"] +windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"] [[package]] name = "txaio" @@ -2423,7 +2423,7 @@ files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -markers = {dev = "python_version < \"3.13\"", release = "python_version < \"3.13\""} +markers = {dev = "python_version == \"3.12\"", release = "python_version == \"3.12\""} [[package]] name = "tzdata" @@ -2451,7 +2451,7 @@ files = [ ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -2487,7 +2487,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "wcwidth" @@ -2653,7 +2653,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.1" python-versions = "^3.12" -# feature/verified-resource-linking -content-hash = "9f3d39b29f00131619e3d005ac8687d5c6189ef2b3e95dc7c3adb18714202b27" -# upstream/develop -# content-hash = "580d926a21f66415a86daa4f9442ffd47d0bf7a0bc2446948ece4349b53db600" +content-hash = "bc1ceda74871d00d3d7b3777b6ba9edfb18c1a3af3497451a2800815818e272a" From 92fd2c02011364d0e5e27707f9cbec9178847bbb Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 30 Apr 2025 09:02:31 -0400 Subject: [PATCH 041/100] Fix migration conflicts from develop --- ...allinkservice_browser_base_url_and_more.py | 21 ------------------- ...nkaccount_configuredlinkaddon_and_more.py} | 5 +++-- 2 files changed, 3 insertions(+), 23 deletions(-) delete mode 100644 addon_service/migrations/0013_externallinkservice_browser_base_url_and_more.py rename addon_service/migrations/{0012_authorizedlinkaccount_configuredlinkaddon_and_more.py => 0014_authorizedlinkaccount_configuredlinkaddon_and_more.py} (93%) diff --git a/addon_service/migrations/0013_externallinkservice_browser_base_url_and_more.py b/addon_service/migrations/0013_externallinkservice_browser_base_url_and_more.py deleted file mode 100644 index fbe304cb..00000000 --- a/addon_service/migrations/0013_externallinkservice_browser_base_url_and_more.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 4.2.7 on 2025-04-17 16:44 - -from django.db import ( - migrations, - models, -) - - -class Migration(migrations.Migration): - - dependencies = [ - ("addon_service", "0012_authorizedlinkaccount_configuredlinkaddon_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="externallinkservice", - name="browser_base_url", - field=models.URLField(blank=True, default=""), - ) - ] diff --git a/addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py b/addon_service/migrations/0014_authorizedlinkaccount_configuredlinkaddon_and_more.py similarity index 93% rename from addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py rename to addon_service/migrations/0014_authorizedlinkaccount_configuredlinkaddon_and_more.py index 87df7bb6..a8ff71fe 100644 --- a/addon_service/migrations/0012_authorizedlinkaccount_configuredlinkaddon_and_more.py +++ b/addon_service/migrations/0014_authorizedlinkaccount_configuredlinkaddon_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.7 on 2025-04-04 12:12 +# Generated by Django 4.2.7 on 2025-04-30 12:43 import django.db.models.deletion from django.db import ( @@ -13,7 +13,7 @@ class Migration(migrations.Migration): dependencies = [ - ("addon_service", "0011_oauth2tokenmetadata_date_last_refreshed"), + ("addon_service", "0013_externalcredentials_int_credentials_format_and_more"), ] operations = [ @@ -82,6 +82,7 @@ class Migration(migrations.Migration): to="addon_service.externalservice", ), ), + ("browser_base_url", models.URLField(blank=True, default="")), ( "int_supported_resource_types", models.BigIntegerField( From 75a14ba36e49841e9be0c26afde3dd487c8020b6 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 30 Apr 2025 12:06:37 -0400 Subject: [PATCH 042/100] Fix resource_type and enum serializer --- addon_service/common/enum_serializers.py | 4 +--- addon_service/configured_addon/link/models.py | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/addon_service/common/enum_serializers.py b/addon_service/common/enum_serializers.py index 6ea1453a..24d9bfdd 100644 --- a/addon_service/common/enum_serializers.py +++ b/addon_service/common/enum_serializers.py @@ -41,9 +41,7 @@ def to_internal_value(self, data) -> enum.Enum: return self.enum_cls[_name] def to_representation(self, value: enum.Enum): - if isinstance(value, str): - value = self.enum_cls[value] - return super().to_representation(value).name + return value.name class EnumNameMultipleChoiceField( diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index 2cbc0f51..40564aee 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -24,8 +24,8 @@ class ConfiguredLinkAddon(ConfiguredAddon): int_resource_type = models.BigIntegerField(validators=[is_supported_resource_type]) @property - def resource_type(self) -> str: - return SupportedResourceTypes(self.int_resource_type).name + def resource_type(self) -> SupportedResourceTypes: + return SupportedResourceTypes(self.int_resource_type) @resource_type.setter def resource_type(self, value) -> None: From 3af8f7f25f06547dcf5a232099ccaad9243871c8 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 5 May 2025 16:23:34 -0400 Subject: [PATCH 043/100] Both target_id and resource_type can be blank or null --- addon_service/configured_addon/link/models.py | 40 +++++++++++-------- ...redlinkaddon_int_resource_type_and_more.py | 35 ++++++++++++++++ 2 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 addon_service/migrations/0015_alter_configuredlinkaddon_int_resource_type_and_more.py diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index 40564aee..01888c48 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -11,6 +11,8 @@ def is_supported_resource_type(resource_type: int): + if not resource_type: + return try: resource_type = SupportedResourceTypes(resource_type) if len(resource_type) != 1: @@ -20,12 +22,16 @@ def is_supported_resource_type(resource_type: int): class ConfiguredLinkAddon(ConfiguredAddon): - target_id = models.CharField() - int_resource_type = models.BigIntegerField(validators=[is_supported_resource_type]) + target_id = models.CharField(default=None, null=True, blank=True) + int_resource_type = models.BigIntegerField( + default=None, null=True, blank=True, validators=[is_supported_resource_type] + ) @property - def resource_type(self) -> SupportedResourceTypes: - return SupportedResourceTypes(self.int_resource_type) + def resource_type(self) -> SupportedResourceTypes | None: + if not self.int_resource_type: + return SupportedResourceTypes(self.int_resource_type) + return None @resource_type.setter def resource_type(self, value) -> None: @@ -33,21 +39,23 @@ def resource_type(self, value) -> None: def save(self, *args, full_clean=True, **kwargs): super().save(*args, full_clean=full_clean, **kwargs) - app.send_task( - "website.identifiers.tasks.task__update_doi_metadata_with_verified_links", - kwargs={"target_guid": self.authorized_resource.guid}, - ) + if self.resource_type and self.target_id: + app.send_task( + "website.identifiers.tasks.task__update_doi_metadata_with_verified_links", + kwargs={"target_guid": self.authorized_resource.guid}, + ) def target_url(self): - if self.target_id: - from addon_service.addon_imp.instantiation import ( - get_link_addon_instance__blocking, - ) + from addon_service.addon_imp.instantiation import ( + get_link_addon_instance__blocking, + ) - addon = get_link_addon_instance__blocking( - self.imp_cls, self.base_account, self.config - ) - return async_to_sync(addon.build_url_for_id)(self.target_id) + if not self.target_id: + return None + addon = get_link_addon_instance__blocking( + self.imp_cls, self.base_account, self.config + ) + return async_to_sync(addon.build_url_for_id)(self.target_id) @property def config(self) -> LinkConfig: diff --git a/addon_service/migrations/0015_alter_configuredlinkaddon_int_resource_type_and_more.py b/addon_service/migrations/0015_alter_configuredlinkaddon_int_resource_type_and_more.py new file mode 100644 index 00000000..887ff3e5 --- /dev/null +++ b/addon_service/migrations/0015_alter_configuredlinkaddon_int_resource_type_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.7 on 2025-05-05 20:22 + +from django.db import ( + migrations, + models, +) + +import addon_service.configured_addon.link.models + + +class Migration(migrations.Migration): + + dependencies = [ + ("addon_service", "0014_authorizedlinkaccount_configuredlinkaddon_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="configuredlinkaddon", + name="int_resource_type", + field=models.BigIntegerField( + blank=True, + default=None, + null=True, + validators=[ + addon_service.configured_addon.link.models.is_supported_resource_type + ], + ), + ), + migrations.AlterField( + model_name="configuredlinkaddon", + name="target_id", + field=models.CharField(blank=True, default=None, null=True), + ), + ] From 9e3b900e1ad2ec4811e40d87a129ee1022334d42 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 5 May 2025 16:26:52 -0400 Subject: [PATCH 044/100] Update ConfiguredLinkAddonSerializer: allow null and blank --- addon_service/configured_addon/link/serializers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index 7ffb4691..d07ee669 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -25,9 +25,11 @@ class ConfiguredLinkAddonSerializer(ConfiguredAddonSerializer): """api serializer for the `ConfiguredLinkAddon` model""" - target_url = URLField(read_only=True) - target_id = CharField() - resource_type = EnumNameChoiceField(enum_cls=SupportedResourceTypes) + target_url = URLField(allow_null=True, allow_blank=True, read_only=True) + target_id = CharField(allow_null=True, allow_blank=True) + resource_type = EnumNameChoiceField( + allow_null=True, allow_blank=True, enum_cls=SupportedResourceTypes + ) connected_operations = DataclassRelatedLinkField( dataclass_model=AddonOperationModel, From 706b7a0ff768ed83936d43b319c04f970238779e Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 5 May 2025 16:29:04 -0400 Subject: [PATCH 045/100] Update VerifiedLinkSerializer * Fix serializer naming * All fields are read-only --- addon_service/configured_addon/link/serializers.py | 10 ++++++---- addon_service/configured_addon/link/views.py | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index d07ee669..e3f4db2a 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -83,7 +83,7 @@ class Meta: ] -class VerifiedLink(serializers.Serializer): +class VerifiedLinkSerializer(serializers.Serializer): """Serialize ConfiguredLinkAddon information required by OSF. The information is shaped for osf to be able to update datacite and share metadata @@ -94,6 +94,8 @@ class JSONAPIMeta: resource_name = "verified-link" target_url = URLField(read_only=True) - target_id = CharField() - resource_type = EnumNameChoiceField(enum_cls=SupportedResourceTypes) - service_name = CharField(source="external_service.external_service_name") + target_id = CharField(read_only=True) + resource_type = EnumNameChoiceField(read_only=True, enum_cls=SupportedResourceTypes) + service_name = CharField( + read_only=True, source="external_service.external_service_name" + ) diff --git a/addon_service/configured_addon/link/views.py b/addon_service/configured_addon/link/views.py index b5825479..bf1a886b 100644 --- a/addon_service/configured_addon/link/views.py +++ b/addon_service/configured_addon/link/views.py @@ -9,7 +9,7 @@ from .models import ConfiguredLinkAddon from .serializers import ( ConfiguredLinkAddonSerializer, - VerifiedLink, + VerifiedLinkSerializer, ) @@ -36,4 +36,4 @@ def get_verified_links(self, request, pk=None): ).select_related("base_account__external_service") self.resource_name = "verified-link" - return Response(VerifiedLink(addons, many=True).data) + return Response(VerifiedLinkSerializer(addons, many=True).data) From 16521c47af98ba05ac6c5679fe82eff1094e3dd1 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 6 May 2025 13:18:20 +0300 Subject: [PATCH 046/100] fixed Hybrid service type not being displayed in admin --- addon_service/admin/_base.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/addon_service/admin/_base.py b/addon_service/admin/_base.py index 5df1fe84..ff84a979 100644 --- a/addon_service/admin/_base.py +++ b/addon_service/admin/_base.py @@ -56,7 +56,7 @@ def formfield_for_dbfield(self, db_field, request, **kwargs): label=db_field.verbose_name, choices=[ (None, ""), - *((_member.value, _member.name) for _member in _enum), + *self.iter_enum_members(_enum), ], ) if ( @@ -65,11 +65,17 @@ def formfield_for_dbfield(self, db_field, request, **kwargs): ): _enum = self.enum_multiple_choice_fields[db_field.name] return EnumNameMultipleChoiceField( - choices=[ - *((_member.value, _member.name) for _member in _enum), - ], + choices=self.iter_enum_members(_enum), widget=forms.CheckboxSelectMultiple, enum_cls=_enum, ) return super().formfield_for_dbfield(db_field, request, **kwargs) + + def iter_enum_members(self, _enum): + if hasattr(_enum, "__members__"): + iterator = _enum.__members__.values() + else: + iterator = _enum + + return [(_member.value, _member.name) for _member in iterator] From 034a34dcb909f4f379c33ab52060a87d7dea8cc1 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 6 May 2025 13:38:04 +0300 Subject: [PATCH 047/100] fixed helper to be private --- addon_service/admin/_base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addon_service/admin/_base.py b/addon_service/admin/_base.py index ff84a979..c1454aca 100644 --- a/addon_service/admin/_base.py +++ b/addon_service/admin/_base.py @@ -56,7 +56,7 @@ def formfield_for_dbfield(self, db_field, request, **kwargs): label=db_field.verbose_name, choices=[ (None, ""), - *self.iter_enum_members(_enum), + *self._list_enum_members(_enum), ], ) if ( @@ -65,14 +65,14 @@ def formfield_for_dbfield(self, db_field, request, **kwargs): ): _enum = self.enum_multiple_choice_fields[db_field.name] return EnumNameMultipleChoiceField( - choices=self.iter_enum_members(_enum), + choices=self._list_enum_members(_enum), widget=forms.CheckboxSelectMultiple, enum_cls=_enum, ) return super().formfield_for_dbfield(db_field, request, **kwargs) - def iter_enum_members(self, _enum): + def _list_enum_members(self, _enum): if hasattr(_enum, "__members__"): iterator = _enum.__members__.values() else: From 9b204309900693f6dfc07195368625ccc02a054f Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 6 May 2025 09:44:08 -0400 Subject: [PATCH 048/100] Fix typo in condition check Co-authored-by: Yuhuai Liu --- addon_service/configured_addon/link/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index 01888c48..fd5d42d9 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -29,7 +29,7 @@ class ConfiguredLinkAddon(ConfiguredAddon): @property def resource_type(self) -> SupportedResourceTypes | None: - if not self.int_resource_type: + if self.int_resource_type: return SupportedResourceTypes(self.int_resource_type) return None From f044698688343233b257ec5ace4889c7946fc588 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 6 May 2025 09:51:19 -0400 Subject: [PATCH 049/100] Use required=False for target_id and resource_type in serializer Co-authored-by: Yuhuai Liu --- addon_service/configured_addon/link/serializers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index e3f4db2a..aab1d534 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -26,9 +26,10 @@ class ConfiguredLinkAddonSerializer(ConfiguredAddonSerializer): """api serializer for the `ConfiguredLinkAddon` model""" target_url = URLField(allow_null=True, allow_blank=True, read_only=True) - target_id = CharField(allow_null=True, allow_blank=True) + target_id = CharField(required=False) resource_type = EnumNameChoiceField( - allow_null=True, allow_blank=True, enum_cls=SupportedResourceTypes + required=False, + enum_cls=SupportedResourceTypes, ) connected_operations = DataclassRelatedLinkField( From e3f0127a55cf4bd0b81a67a06893cf0f50f0bcc9 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 6 May 2025 11:30:16 -0400 Subject: [PATCH 050/100] Add comments on why we eased restrictions due to FE limitation --- addon_service/configured_addon/link/models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index fd5d42d9..085a1394 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -22,6 +22,10 @@ def is_supported_resource_type(resource_type: int): class ConfiguredLinkAddon(ConfiguredAddon): + # Note: ideally, `target_id` and `int_resource_type` are both required and + # should be present on first save. However, due to a limitation on how our + # FE workflow is currently wired, a `ConfiguredLinkAddon` instance must be + # saved before both attributes can be set. Thus, we allow null and blank. target_id = models.CharField(default=None, null=True, blank=True) int_resource_type = models.BigIntegerField( default=None, null=True, blank=True, validators=[is_supported_resource_type] @@ -39,6 +43,7 @@ def resource_type(self, value) -> None: def save(self, *args, full_clean=True, **kwargs): super().save(*args, full_clean=full_clean, **kwargs) + # Only send for OSF task after the addon has been fully configured if self.resource_type and self.target_id: app.send_task( "website.identifiers.tasks.task__update_doi_metadata_with_verified_links", From dcf47fa7268c5fd11c3589d59073f102596533a1 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 6 May 2025 11:33:46 -0400 Subject: [PATCH 051/100] Skip allow_null and allow_blank for read-only fields in serializer --- addon_service/configured_addon/link/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index aab1d534..ae273f69 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -25,7 +25,7 @@ class ConfiguredLinkAddonSerializer(ConfiguredAddonSerializer): """api serializer for the `ConfiguredLinkAddon` model""" - target_url = URLField(allow_null=True, allow_blank=True, read_only=True) + target_url = URLField(read_only=True) target_id = CharField(required=False) resource_type = EnumNameChoiceField( required=False, From b9983ebc960123ffaac2574235ddbd58a4053a0c Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 7 May 2025 11:45:10 +0300 Subject: [PATCH 052/100] fixed requests failing when dataverses contain unpublished datasets --- addon_imps/link/dataverse.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/addon_imps/link/dataverse.py b/addon_imps/link/dataverse.py index 9f6ee80d..23f513ae 100644 --- a/addon_imps/link/dataverse.py +++ b/addon_imps/link/dataverse.py @@ -3,6 +3,7 @@ import asyncio import re from dataclasses import dataclass +from http import HTTPStatus from typing import Literal from django.core.exceptions import ValidationError @@ -122,12 +123,13 @@ async def _fetch_dataverse_items(self, dataverse_id) -> list[ItemResult]: f"api/dataverses/{dataverse_id}/contents" ) as response: response_content = await response.json_content() - return await asyncio.gather( + items = await asyncio.gather( *[ self._get_dataverse_or_dataset_item(item) for item in response_content["data"] ] ) + return [item for item in items if item] async def _get_dataverse_or_dataset_item(self, item: dict): match item["type"]: @@ -152,10 +154,12 @@ async def _fetch_dataset_with_parser( dataset_id: str = None, persistent_id: str = None, parser=None, - ) -> ItemResult | list[ItemResult]: + ) -> ItemResult | list[ItemResult] | None: url = f"api/datasets/{':persistentId' if persistent_id else dataset_id}/versions/:latest-published" query = {"persistentId": persistent_id} if persistent_id else {} async with self.network.GET(url, query=query) as response: + if response.http_status == HTTPStatus.NOT_FOUND: + return None return parser(await response.json_content()) async def _fetch_dataset( From 01a27860a61eada635b73d559b639b1d5f8124bb Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Sun, 11 May 2025 21:00:45 -0400 Subject: [PATCH 053/100] fix --- addon_service/configured_addon/link/models.py | 3 ++- addon_service/configured_addon/link/serializers.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index 085a1394..a33efa4b 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -39,7 +39,8 @@ def resource_type(self) -> SupportedResourceTypes | None: @resource_type.setter def resource_type(self, value) -> None: - self.int_resource_type = value.value + if value is not None: + self.int_resource_type = value.value def save(self, *args, full_clean=True, **kwargs): super().save(*args, full_clean=full_clean, **kwargs) diff --git a/addon_service/configured_addon/link/serializers.py b/addon_service/configured_addon/link/serializers.py index ae273f69..54558e37 100644 --- a/addon_service/configured_addon/link/serializers.py +++ b/addon_service/configured_addon/link/serializers.py @@ -26,9 +26,10 @@ class ConfiguredLinkAddonSerializer(ConfiguredAddonSerializer): """api serializer for the `ConfiguredLinkAddon` model""" target_url = URLField(read_only=True) - target_id = CharField(required=False) + target_id = CharField(required=False, allow_null=True) resource_type = EnumNameChoiceField( required=False, + allow_null=True, enum_cls=SupportedResourceTypes, ) From 7d311e1a2afbad2bebc2c079f1d6e3d7c4b6f15e Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 12 May 2025 12:24:53 -0400 Subject: [PATCH 054/100] Fix naming for OutputManagementPlan --- addon_toolkit/interfaces/link.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon_toolkit/interfaces/link.py b/addon_toolkit/interfaces/link.py index f55e48a3..0085b35c 100644 --- a/addon_toolkit/interfaces/link.py +++ b/addon_toolkit/interfaces/link.py @@ -53,7 +53,7 @@ class SupportedResourceTypes(enum.Flag): Journal = enum.auto() JournalArticle = enum.auto() Model = enum.auto() - OutputManagement_plan = enum.auto() + OutputManagementPlan = enum.auto() PeerReview = enum.auto() PhysicalObject = enum.auto() Preprint = enum.auto() From 5a228fa9f5e932138add5ef80a3c1b60ed356de6 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Thu, 15 May 2025 15:41:59 +0300 Subject: [PATCH 055/100] fixed invalid serializer for AuthorizedLinkAccounts accessed from UserReference --- addon_service/user_reference/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon_service/user_reference/serializers.py b/addon_service/user_reference/serializers.py index 70df89e8..5d5e4215 100644 --- a/addon_service/user_reference/serializers.py +++ b/addon_service/user_reference/serializers.py @@ -63,7 +63,7 @@ class UserReferenceSerializer(serializers.HyperlinkedModelSerializer): "addon_service.serializers.AuthorizedComputingAccountSerializer" ), "authorized_link_accounts": ( - "addon_service.serializers.AuthorizedComputingAccountSerializer" + "addon_service.serializers.AuthorizedLinkAccountSerializer" ), "configured_resources": ( "addon_service.serializers.ResourceReferenceSerializer" From 387b79cafdc6171b042b51e1e83467ea4ee67d56 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 16 May 2025 13:06:51 +0300 Subject: [PATCH 056/100] made EnumNameMultipleChoiceField sorted --- addon_service/common/enum_serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon_service/common/enum_serializers.py b/addon_service/common/enum_serializers.py index 24d9bfdd..476531c9 100644 --- a/addon_service/common/enum_serializers.py +++ b/addon_service/common/enum_serializers.py @@ -61,4 +61,4 @@ def to_internal_value(self, data) -> enum.Flag: def to_representation(self, value): _member_list = super().to_representation(value) - return [_member.name for _member in _member_list] + return sorted([_member.name for _member in _member_list]) From c876d883a572e98c97da01a60a3392a3c8a2ad41 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 20 May 2025 13:21:25 +0300 Subject: [PATCH 057/100] added dataverse to fill_external_services script --- .../commands/fill_external_services.py | 2 ++ .../commands/providers/providers.csv | 31 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/addon_service/management/commands/fill_external_services.py b/addon_service/management/commands/fill_external_services.py index 081e986d..d4de9690 100644 --- a/addon_service/management/commands/fill_external_services.py +++ b/addon_service/management/commands/fill_external_services.py @@ -6,6 +6,7 @@ from addon_service.external_service.citation.models import ExternalCitationService from addon_service.external_service.computing.models import ExternalComputingService +from addon_service.external_service.link.models import ExternalLinkService from addon_service.external_service.storage.models import ExternalStorageService from addon_service.oauth1 import OAuth1ClientConfig from addon_service.oauth2 import OAuth2ClientConfig @@ -20,6 +21,7 @@ "storage": ExternalStorageService, "citation": ExternalCitationService, "computing": ExternalComputingService, + "link": ExternalLinkService, } icons_path = Path(__file__).parent.parent.parent / "static/provider_icons" diff --git a/addon_service/management/commands/providers/providers.csv b/addon_service/management/commands/providers/providers.csv index 27c077c4..62594c38 100644 --- a/addon_service/management/commands/providers/providers.csv +++ b/addon_service/management/commands/providers/providers.csv @@ -1,15 +1,16 @@ -id,display_name,int_credentials_format,int_service_type,int_addon_imp,supported_scopes,api_base_url,wb_key,oauth1_client_config_id,oauth2_client_config_id,icon_name,api_base_url_options,max_concurrent_downloads,max_upload_mb,int_supported_features,service_type -d424bec8-811b-4d23-bf1c-f4e8219e0454,Mendeley,1,1,1004,{all},https://api.mendeley.com,mendeley,,b3fd76fc-3368-47b4-9053-ff4b7ca4baa2,mendeley.svg,,,,3,citation -fce6ada4-9a6e-4986-94c8-9a378170f5fb,Zotero,5,1,1002,{all},https://api.zotero.org,zotero_org,e3663c71-3520-4fce-b0ff-e56c81a73d54,,zotero.svg,,,,3,citation -455cfb5f-6ca0-4d60-8fc8-279f7b229da2,Boa,3,1,1020,{all},https://boa.com,boa,,,boa.png,,,,1,computing -d5ad0101-b732-4a08-8306-384d66beef0e,Gitlab,4,1,1011,{},https://gitlab.com,gitlab,,,gitlab.svg,{},10,10,511,storage -e3cb1bc9-83ec-49fa-86de-1d04d3f94834,Dataverse,6,3,1010,{},https://demo.dataverse.org,dataverse,,,dataverse.svg,,10,10,511,storage -4a2aa5c7-c5be-4e3f-abd8-04fb34928bd3,s3,2,1,1003,{},https://api.github.com,s3,,,s3.svg,,10,10,511,storage -4854b243-593a-4c6f-96e8-5bb2a2df9134,BOX,1,1,1001,{root_readwrite},https://api.box.com/2.0/,box,,4258d732-db77-42aa-b275-8da841adf77f,box.svg,,10,10,2047,storage -9e7148ae-4919-4676-bb44-a7a6662c6dd0,Dropbox,1,1,1006,{files.metadata.write files.content.write files.content.read files.metadata.read},https://api.dropboxapi.com/2/,dropbox,,90b44f3d-66e3-444c-9261-534b9e37b21a,dropbox.svg,,10,10,511,storage -15b66b0c-4db9-4425-b2e0-dfd701a55b05,OneDrive,1,1,1008,{User.Read offline_access Files.Read Files.Read.All Files.ReadWrite},https://graph.microsoft.com/v1.0/,onedrive,,a64e222c-a37b-4f34-9f27-83611d851f5c,onedrive.svg,,10,10,2047,storage -babfcfcc-0e47-4c8d-9ca1-3b335bb8cf55,Github,1,1,1013,{repo contents},https://api.github.com,github,,285f28ee-15b5-429e-b7ba-3371a19678a8,github.svg,,10,10,2047,storage -b670cf7a-752f-49ed-8367-8238cf8a24d1,Figshare,1,1,1007,{all},https://api.figshare.com/v2/,figshare,,5c161353-7444-478e-881f-3b8aec3a002a,figshare.svg,,10,10,511,storage -2acd3e09-5e27-4e5b-a3d1-579f16663cfc,Bitbucket,1,1,1012,{account repository team},https://api.bitbucket.org/2.0/,bitbucket,,1b2d71cc-425e-4aa6-886c-e2ff247c4f2c,bitbucket.svg,,10,10,511,storage -102c6356-e31a-4316-a9fe-8b2bcb20fdd6,Google Drive,1,1,1005,{https://www.googleapis.com/auth/drive},https://www.googleapis.com/,googledrive,,5cf93c06-c27f-4446-b8ac-ffac209ba465,google_drive.svg,,10,10,511,storage -5bc440c8-a03a-4aa6-bd9e-856aee9883e9,Owncloud,3,2,1009,{},,owncloud,,,owncloud.svg,,10,10,511,storage +id,display_name,int_credentials_format,int_service_type,int_addon_imp,supported_scopes,api_base_url,wb_key,oauth1_client_config_id,oauth2_client_config_id,icon_name,api_base_url_options,max_concurrent_downloads,max_upload_mb,int_supported_features,int_supported_resource_types,browser_base_url,service_type +d424bec8-811b-4d23-bf1c-f4e8219e0454,Mendeley,1,1,1004,{all},https://api.mendeley.com,mendeley,,b3fd76fc-3368-47b4-9053-ff4b7ca4baa2,mendeley.svg,,,,3,,,citation +fce6ada4-9a6e-4986-94c8-9a378170f5fb,Zotero,5,1,1002,{all},https://api.zotero.org,zotero_org,e3663c71-3520-4fce-b0ff-e56c81a73d54,,zotero.svg,,,,3,,,citation +455cfb5f-6ca0-4d60-8fc8-279f7b229da2,Boa,3,1,1020,{all},https://boa.com,boa,,,boa.png,,,,1,,,computing +d5ad0101-b732-4a08-8306-384d66beef0e,Gitlab,4,1,1011,{},https://gitlab.com,gitlab,,,gitlab.svg,{},10,10,511,,,storage +e3cb1bc9-83ec-49fa-86de-1d04d3f94834,Dataverse,6,3,1010,{},https://demo.dataverse.org,dataverse,,,dataverse.svg,,10,10,511,,,storage +4a2aa5c7-c5be-4e3f-abd8-04fb34928bd3,s3,2,1,1003,{},https://api.github.com,s3,,,s3.svg,,10,10,511,,,storage +4854b243-593a-4c6f-96e8-5bb2a2df9134,BOX,1,1,1001,{root_readwrite},https://api.box.com/2.0/,box,,4258d732-db77-42aa-b275-8da841adf77f,box.svg,,10,10,2047,,,storage +9e7148ae-4919-4676-bb44-a7a6662c6dd0,Dropbox,1,1,1006,{files.metadata.write files.content.write files.content.read files.metadata.read},https://api.dropboxapi.com/2/,dropbox,,90b44f3d-66e3-444c-9261-534b9e37b21a,dropbox.svg,,10,10,511,,,storage +15b66b0c-4db9-4425-b2e0-dfd701a55b05,OneDrive,1,1,1008,{User.Read offline_access Files.Read Files.Read.All Files.ReadWrite},https://graph.microsoft.com/v1.0/,onedrive,,a64e222c-a37b-4f34-9f27-83611d851f5c,onedrive.svg,,10,10,2047,,,storage +babfcfcc-0e47-4c8d-9ca1-3b335bb8cf55,Github,1,1,1013,{repo contents},https://api.github.com,github,,285f28ee-15b5-429e-b7ba-3371a19678a8,github.svg,,10,10,2047,,,storage +b670cf7a-752f-49ed-8367-8238cf8a24d1,Figshare,1,1,1007,{all},https://api.figshare.com/v2/,figshare,,5c161353-7444-478e-881f-3b8aec3a002a,figshare.svg,,10,10,511,,,storage +2acd3e09-5e27-4e5b-a3d1-579f16663cfc,Bitbucket,1,1,1012,{account repository team},https://api.bitbucket.org/2.0/,bitbucket,,1b2d71cc-425e-4aa6-886c-e2ff247c4f2c,bitbucket.svg,,10,10,511,,,storage +102c6356-e31a-4316-a9fe-8b2bcb20fdd6,Google Drive,1,1,1005,{https://www.googleapis.com/auth/drive},https://www.googleapis.com/,googledrive,,5cf93c06-c27f-4446-b8ac-ffac209ba465,google_drive.svg,,10,10,511,,,storage +5bc440c8-a03a-4aa6-bd9e-856aee9883e9,Owncloud,3,2,1009,{},,owncloud,,,owncloud.svg,,10,10,511,,,storage +edf57d0c-ae0a-4c85-b759-017fc8b009f0,Link dataverse,6,2,1030,{},https://demo.dataverse.org,,,,dataverse.svg,{},link,,,4294967295,,link From 338020b25cdeed72aa760aeca31407f78b5ab096 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Fri, 23 May 2025 09:50:04 -0400 Subject: [PATCH 058/100] Reorder KnownAddonImps and AddonImpNumbers --- addon_service/common/known_imps.py | 47 +++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/addon_service/common/known_imps.py b/addon_service/common/known_imps.py index e6b49653..8692e99e 100644 --- a/addon_service/common/known_imps.py +++ b/addon_service/common/known_imps.py @@ -72,25 +72,34 @@ def get_imp_number(imp: type[AddonImp]) -> int: @enum.unique class KnownAddonImps(enum.Enum): - """Static mapping from API-facing name for an AddonImp to the Imp itself""" + """Static mapping from API-facing name for an AddonImp to the Imp itself. + Note: Grouped by type and then ordered by respective AddonImpNumbers. + """ + + # Type: Storage BOX = box_dot_com.BoxDotComStorageImp S3 = s3.S3StorageImp - ONEDRIVE = onedrive.OneDriveStorageImp - ZOTERO = zotero_org.ZoteroOrgCitationImp GOOGLEDRIVE = google_drive.GoogleDriveStorageImp + DROPBOX = dropbox.DropboxStorageImp FIGSHARE = figshare.FigshareStorageImp - MENDELEY = mendeley.MendeleyCitationImp - BITBUCKET = bitbucket.BitbucketStorageImp - DATAVERSE = dataverse.DataverseStorageImp + ONEDRIVE = onedrive.OneDriveStorageImp OWNCLOUD = owncloud.OwnCloudStorageImp - LINK_DATAVERSE = link_dataverse.DataverseLinkImp - GITHUB = github.GitHubStorageImp + DATAVERSE = dataverse.DataverseStorageImp GITLAB = gitlab.GitlabStorageImp - DROPBOX = dropbox.DropboxStorageImp + BITBUCKET = bitbucket.BitbucketStorageImp + GITHUB = github.GitHubStorageImp + # Type: Citation + ZOTERO = zotero_org.ZoteroOrgCitationImp + MENDELEY = mendeley.MendeleyCitationImp + + # Type: Cloud Computing BOA = boa.BoaComputingImp + # Type: Link + LINK_DATAVERSE = link_dataverse.DataverseLinkImp + if __debug__: BLARG = my_blarg.MyBlargStorage @@ -98,12 +107,16 @@ class KnownAddonImps(enum.Enum): @enum_names_same_as(KnownAddonImps) @enum.unique class AddonImpNumbers(enum.Enum): - """Static mapping from each AddonImp name to a unique integer (for database use)""" + """Static mapping from each AddonImp name to a unique integer (for database use) + Note: Ideally, we should "prefix" the number by type and take future scalability into account. + e.g. Storage type uses 10xx, Citation type uses 11xx, Cloud Computing type uses 12xx and LINK type uses 13xx. + Consider this a future improvement when we run out of 1001~1019 for both storage and citation addons. + """ + + # Type: Storage BOX = 1001 - ZOTERO = 1002 S3 = 1003 - MENDELEY = 1004 GOOGLEDRIVE = 1005 DROPBOX = 1006 FIGSHARE = 1007 @@ -112,12 +125,18 @@ class AddonImpNumbers(enum.Enum): DATAVERSE = 1010 GITLAB = 1011 BITBUCKET = 1012 - - LINK_DATAVERSE = 1030 GITHUB = 1013 + # Type: Citation + ZOTERO = 1002 + MENDELEY = 1004 + + # Type: Cloud Computing BOA = 1020 + # Type: Link + LINK_DATAVERSE = 1030 + if __debug__: BLARG = -7 From 1b3516f2699e21852c1b6dd0b9532f1d9fc78327 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 27 May 2025 10:06:40 -0400 Subject: [PATCH 059/100] Add int_supported_features to link type external service --- addon_service/admin/__init__.py | 6 ++- addon_service/external_service/link/models.py | 41 ++++++++++++++++++- .../external_service/link/serializers.py | 10 ++++- ...service_int_supported_features_and_more.py | 38 +++++++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 addon_service/migrations/0016_externallinkservice_int_supported_features_and_more.py diff --git a/addon_service/admin/__init__.py b/addon_service/admin/__init__.py index 59347d5c..10389538 100644 --- a/addon_service/admin/__init__.py +++ b/addon_service/admin/__init__.py @@ -8,7 +8,10 @@ from addon_service.external_service.storage.models import StorageSupportedFeatures from ..external_service.citation.models import CitationSupportedFeatures -from ..external_service.link.models import SupportedResourceTypes +from ..external_service.link.models import ( + LinkSupportedFeatures, + SupportedResourceTypes, +) from ._base import GravyvaletModelAdmin from .decorators import linked_many_field @@ -66,6 +69,7 @@ class ExternalLinkServiceAdmin(GravyvaletModelAdmin): "int_service_type": ServiceTypes, } enum_multiple_choice_fields = { + "int_supported_features": LinkSupportedFeatures, "int_supported_resource_types": SupportedResourceTypes, } diff --git a/addon_service/external_service/link/models.py b/addon_service/external_service/link/models.py index 8ebd148d..41453389 100644 --- a/addon_service/external_service/link/models.py +++ b/addon_service/external_service/link/models.py @@ -1,3 +1,8 @@ +from enum import ( + Flag, + auto, +) + from django.core.exceptions import ValidationError from django.db import models @@ -10,19 +15,51 @@ from addon_toolkit.interfaces.link import SupportedResourceTypes +# TODO: need to trim down the features +class LinkSupportedFeatures(Flag): + ADD_UPDATE_FILES = auto() + ADD_UPDATE_FILES_PARTIAL = auto() + DELETE_FILES = auto() + DELETE_FILES_PARTIAL = auto() + FORKING = auto() + LOGS = auto() + PERMISSIONS = auto() + REGISTERING = auto() + FILE_VERSIONS = auto() + COPY_INTO = auto() + DOWNLOAD_AS_ZIP = auto() + + def validate_supported_features(value): + _validate_enum_value(LinkSupportedFeatures, value) + + +def validate_supported_resource_types(value): _validate_enum_value(SupportedResourceTypes, value) class ExternalLinkService(ExternalService): browser_base_url = models.URLField(blank=True, default="") - int_supported_resource_types = models.BigIntegerField( + int_supported_features = models.IntegerField( validators=[validate_supported_features], null=True ) + int_supported_resource_types = models.BigIntegerField( + validators=[validate_supported_resource_types], null=True + ) @property - def supported_resource_types(self) -> list[SupportedResourceTypes]: + def supported_features(self) -> list[LinkSupportedFeatures]: """get the enum representation of int_supported_features""" + return LinkSupportedFeatures(self.int_supported_features) + + @supported_features.setter + def supported_features(self, new_supported_features: LinkSupportedFeatures): + """set int_authorized_capabilities without caring its int""" + self.int_supported_features = new_supported_features.value + + @property + def supported_resource_types(self) -> list[SupportedResourceTypes]: + """get the enum representation of int_supported_resource_types""" return SupportedResourceTypes(self.int_supported_resource_types) @supported_resource_types.setter diff --git a/addon_service/external_service/link/serializers.py b/addon_service/external_service/link/serializers.py index 556a3175..c42a0a5a 100644 --- a/addon_service/external_service/link/serializers.py +++ b/addon_service/external_service/link/serializers.py @@ -5,7 +5,10 @@ from addon_service.common import view_names from addon_service.common.enum_serializers import EnumNameMultipleChoiceField from addon_service.common.serializer_fields import DataclassRelatedDataField -from addon_service.external_service.link.models import SupportedResourceTypes +from addon_service.external_service.link.models import ( + LinkSupportedFeatures, + SupportedResourceTypes, +) from addon_service.external_service.serializers import ExternalServiceSerializer from .models import ExternalLinkService @@ -26,6 +29,10 @@ class ExternalLinkServiceSerializer(ExternalServiceSerializer): related_link_view_name=view_names.related_view(RESOURCE_TYPE), ) + supported_features = EnumNameMultipleChoiceField( + enum_cls=LinkSupportedFeatures, read_only=True + ) + supported_resource_types = EnumNameMultipleChoiceField( enum_cls=SupportedResourceTypes, read_only=True ) @@ -43,6 +50,7 @@ class Meta: "external_service_name", "configurable_api_root", "supported_resource_types", + "supported_features", "icon_url", "api_base_url_options", ] diff --git a/addon_service/migrations/0016_externallinkservice_int_supported_features_and_more.py b/addon_service/migrations/0016_externallinkservice_int_supported_features_and_more.py new file mode 100644 index 00000000..e75123b2 --- /dev/null +++ b/addon_service/migrations/0016_externallinkservice_int_supported_features_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2.20 on 2025-05-27 13:57 + +from django.db import ( + migrations, + models, +) + +import addon_service.external_service.link.models + + +class Migration(migrations.Migration): + + dependencies = [ + ("addon_service", "0015_alter_configuredlinkaddon_int_resource_type_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="externallinkservice", + name="int_supported_features", + field=models.IntegerField( + null=True, + validators=[ + addon_service.external_service.link.models.validate_supported_features + ], + ), + ), + migrations.AlterField( + model_name="externallinkservice", + name="int_supported_resource_types", + field=models.BigIntegerField( + null=True, + validators=[ + addon_service.external_service.link.models.validate_supported_resource_types + ], + ), + ), + ] From 2b53b15633989e44bc200bc7c36145f5501d8a27 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 27 May 2025 17:38:29 +0300 Subject: [PATCH 060/100] renamed task__update_doi_metadata_with_verified_links --- addon_service/configured_addon/link/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon_service/configured_addon/link/models.py b/addon_service/configured_addon/link/models.py index a33efa4b..c16f4dbf 100644 --- a/addon_service/configured_addon/link/models.py +++ b/addon_service/configured_addon/link/models.py @@ -47,7 +47,7 @@ def save(self, *args, full_clean=True, **kwargs): # Only send for OSF task after the addon has been fully configured if self.resource_type and self.target_id: app.send_task( - "website.identifiers.tasks.task__update_doi_metadata_with_verified_links", + "website.identifiers.tasks.task__update_verified_links", kwargs={"target_guid": self.authorized_resource.guid}, ) From f6c088b8ba376e7728eb8c445da8071f04b66b74 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Tue, 27 May 2025 14:47:04 -0400 Subject: [PATCH 061/100] Update supported features for LINK external service --- addon_service/external_service/link/models.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/addon_service/external_service/link/models.py b/addon_service/external_service/link/models.py index 41453389..35b627e0 100644 --- a/addon_service/external_service/link/models.py +++ b/addon_service/external_service/link/models.py @@ -15,19 +15,10 @@ from addon_toolkit.interfaces.link import SupportedResourceTypes -# TODO: need to trim down the features class LinkSupportedFeatures(Flag): - ADD_UPDATE_FILES = auto() - ADD_UPDATE_FILES_PARTIAL = auto() - DELETE_FILES = auto() - DELETE_FILES_PARTIAL = auto() - FORKING = auto() - LOGS = auto() + FORKING_PARTIAL = auto() + LOGS_PARTIAL = auto() PERMISSIONS = auto() - REGISTERING = auto() - FILE_VERSIONS = auto() - COPY_INTO = auto() - DOWNLOAD_AS_ZIP = auto() def validate_supported_features(value): From b7fb8cf8674e8a553241014ea0f095e58d6f730d Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 28 May 2025 12:10:52 -0400 Subject: [PATCH 062/100] Fix supported features for link type external service --- addon_service/external_service/link/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addon_service/external_service/link/models.py b/addon_service/external_service/link/models.py index 35b627e0..ef510025 100644 --- a/addon_service/external_service/link/models.py +++ b/addon_service/external_service/link/models.py @@ -16,9 +16,15 @@ class LinkSupportedFeatures(Flag): + ADD_UPDATE_FILES = auto() + DELETE_FILES = auto() + FORKING = auto() FORKING_PARTIAL = auto() + LOGS = auto() LOGS_PARTIAL = auto() PERMISSIONS = auto() + REGISTERING = auto() + FILE_VERSIONS = auto() def validate_supported_features(value): From 3ca14831b65a25e5c60ce9c92f2dab4c9a164dd0 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 18 Jun 2025 16:24:04 +0300 Subject: [PATCH 063/100] fixed dataverse provider to make dataverse work as link --- addon_imps/link/dataverse.py | 38 ++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/addon_imps/link/dataverse.py b/addon_imps/link/dataverse.py index 23f513ae..84d42fb8 100644 --- a/addon_imps/link/dataverse.py +++ b/addon_imps/link/dataverse.py @@ -16,7 +16,7 @@ ) -DATAVERSE_REGEX = re.compile(r"^dataverse/(?P\d*)$") +DATAVERSE_REGEX = re.compile(r"^dataverse/(?P.*)$") DATASET_REGEX = re.compile(r"^dataset/(?P.*)$") FILE_REGEX = re.compile(r"^file/(?P.*)$") @@ -29,19 +29,31 @@ class DataverseLinkImp(LinkAddonHttpRequestorImp): """ async def build_url_for_id(self, item_id: str) -> str: - if match := DATASET_REGEX.match(item_id): + if match := DATAVERSE_REGEX.match(item_id): + entity_type = "dataverse" + elif match := DATASET_REGEX.match(item_id): entity_type = "dataset" elif match := FILE_REGEX.match(item_id): entity_type = "file" else: raise ValidationError(f"Invalid {item_id=}") - persistent_id = match["persistent_id"] + return self._make_url(entity_type, **match.groupdict()) - return self._make_url(entity_type, persistent_id) + def _make_url( + self, + entity_type: Literal["file", "dataset", "dataverse"], + persistent_id: str = None, + id_: str = None, + ): + if bool(id_) == bool(persistent_id): + raise ValueError("You must pass either id or persistent_id to build url") - def _make_url(self, entity_type: Literal["file", "dataset"], persistent_id): - return f"{self.config.external_web_url}/{entity_type}.xhtml?persistentId={persistent_id}" + if persistent_id: + suffix = f".xhtml?persistentId={persistent_id}" + else: + suffix = f"/{id_}" + return f"{self.config.external_web_url}/{entity_type}{suffix}" async def get_external_account_id(self, _: dict[str, str]) -> str: try: @@ -81,7 +93,7 @@ async def get_item_info(self, item_id: str) -> ItemResult: if not item_id: return ItemResult(item_id="", item_name="", item_type=ItemType.FOLDER) elif match := DATAVERSE_REGEX.match(item_id): - entity = await self._fetch_dataverse(match["id"]) + entity = await self._fetch_dataverse(match["id_"]) elif match := DATASET_REGEX.match(item_id): entity = await self._fetch_dataset( dataset_id=match["id"], persistent_id=match["persistent_id"] @@ -102,7 +114,7 @@ async def list_child_items( if not item_id: return await self.list_root_items(page_cursor) elif match := DATAVERSE_REGEX.match(item_id): - items = await self._fetch_dataverse_items(match["id"]) + items = await self._fetch_dataverse_items(match["id_"]) return ItemSampleResult( items=items, total_count=len(items), @@ -136,7 +148,7 @@ async def _get_dataverse_or_dataset_item(self, item: dict): case "dataset": return await self._fetch_dataset(dataset_id=item["id"]) case "dataverse": - return parse_dataverse_as_subitem(item) + return await self._fetch_dataverse(item["id"]) raise ValueError(f"Invalid item type: {item['type']}") async def _fetch_file(self, dataverse_id) -> ItemResult: @@ -235,7 +247,7 @@ def parse_dataverse(data: dict): return ItemResult( item_type=ItemType.FOLDER, item_name=data["name"], - item_id=f'dataverse/{data["id"]}', + item_id=f'dataverse/{data["alias"]}', ) @@ -245,11 +257,11 @@ def parse_mydata(data: dict): return ItemSampleResult( items=[ ItemResult( - item_id=f"dataverse/{file['entity_id']}", - item_name=file["name"], + item_id=f"dataverse/{item['identifier']}", + item_name=item["name"], item_type=ItemType.FOLDER, ) - for file in data["items"] + for item in data["items"] ], total_count=data["total_count"], next_sample_cursor=( From 8a3091e6ad8ceb6f4d94cac1a3928c086cd02ec9 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Thu, 19 Jun 2025 20:23:56 -0400 Subject: [PATCH 064/100] Fix DATASET_REGEX.match in get_item_info() for linked services --- addon_imps/link/dataverse.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/addon_imps/link/dataverse.py b/addon_imps/link/dataverse.py index 84d42fb8..ba39a140 100644 --- a/addon_imps/link/dataverse.py +++ b/addon_imps/link/dataverse.py @@ -95,9 +95,7 @@ async def get_item_info(self, item_id: str) -> ItemResult: elif match := DATAVERSE_REGEX.match(item_id): entity = await self._fetch_dataverse(match["id_"]) elif match := DATASET_REGEX.match(item_id): - entity = await self._fetch_dataset( - dataset_id=match["id"], persistent_id=match["persistent_id"] - ) + entity = await self._fetch_dataset(persistent_id=match["persistent_id"]) elif match := FILE_REGEX.match(item_id): entity = await self._fetch_file(match["persistent_id"]) else: From d7091cc347f8dd53b18818d21059dc24e3ff4e75 Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Fri, 20 Jun 2025 18:07:00 +0300 Subject: [PATCH 065/100] Fix params for log_gv_addon task --- addon_service/configured_addon/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon_service/configured_addon/models.py b/addon_service/configured_addon/models.py index be3727e5..ce91544c 100644 --- a/addon_service/configured_addon/models.py +++ b/addon_service/configured_addon/models.py @@ -96,8 +96,8 @@ def save(self, *args, full_clean=True, **kwargs): app.send_task( "osf.tasks.log_gv_addon", kwargs={ - "node_guid": self.authorized_resource.guid, - "user_guid": self.account_owner.guid, + "node_url": self.resource_uri, + "user_url": self.owner_uri, "addon": self.display_name, "action": "addon_added", }, From bc1954b53baccff5b3e4a8c3e36bdb0ffb906fb5 Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Tue, 17 Jun 2025 16:22:42 +0300 Subject: [PATCH 066/100] Add PR template --- PULL_REQUEST_TEMPLATE.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..4011a250 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,27 @@ +## Ticket + +[//]: # (Link to a JIRA ticket if available.) + +## Purpose + +[//]: # (Briefly describe the purpose of this PR.) + +## Changes + +[//]: # (Briefly describe or list your changes.) + +## Side Effects + +[//]: # (Any possible side effects?) + +## API Documentation + +[//]: # (Any API documentation updates?) + +## QE Notes + +[//]: # (Any QA testing notes for QE?) + +## CE Notes + +[//]: # (Any server configuration and deployment notes for CE?) From 8f7c9a84572cc993a2f87c112e2200d3daa9eadc Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Wed, 16 Apr 2025 13:27:29 +0300 Subject: [PATCH 067/100] Add new factories for link addons --- addon_service/tests/_factories.py | 145 ++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/addon_service/tests/_factories.py b/addon_service/tests/_factories.py index 749a6cbf..d26b1409 100644 --- a/addon_service/tests/_factories.py +++ b/addon_service/tests/_factories.py @@ -187,3 +187,148 @@ def _create( *args, **kwargs, ) + + +### +# "Link" models + + +class ExternalLinkServiceFactory(DjangoModelFactory): + class Meta: + model = db.ExternalLinkService + + display_name = factory.Faker("word") + int_addon_imp = known_imps.get_imp_number( + known_imps.get_imp_by_name("LINK_DATAVERSE") + ) + supported_scopes = ["service.url/grant_all"] + + @classmethod + def _create( + cls, + model_class, + credentials_format=CredentialsFormats.PERSONAL_ACCESS_TOKEN, + service_type=ServiceTypes.PUBLIC, + supported_resource_types=None, + *args, + **kwargs, + ): + from addon_toolkit.interfaces.link import SupportedResourceTypes + + api_base_url = "" + if ServiceTypes.PUBLIC in service_type: + api_base_url = "https://api.example.url/v1" + + supported_resource_types = ( + supported_resource_types or SupportedResourceTypes.DATASET + ) + + return super()._create( + model_class=model_class, + int_credentials_format=credentials_format.value, + int_service_type=service_type.value, + int_supported_resource_types=supported_resource_types.value, + api_base_url=api_base_url, + *args, + **kwargs, + ) + + +class ExternalLinkOAuth2ServiceFactory(ExternalLinkServiceFactory): + credentials_format = CredentialsFormats.OAUTH2 + oauth2_client_config = factory.SubFactory(OAuth2ClientConfigFactory) + + +class ExternalLinkOAuth1ServiceFactory(ExternalLinkServiceFactory): + credentials_format = CredentialsFormats.OAUTH1A + oauth1_client_config = factory.SubFactory(OAuth1ClientConfigFactory) + + +class AuthorizedLinkAccountFactory(DjangoModelFactory): + class Meta: + model = db.AuthorizedLinkAccount + + display_name = factory.Faker("word") + authorized_capabilities = AddonCapabilities.ACCESS | AddonCapabilities.UPDATE + + @classmethod + def _create( + cls, + model_class, + account_owner=None, + external_service=None, + credentials=None, + credentials_format=CredentialsFormats.OAUTH2, + authorized_scopes=None, + *args, + **kwargs, + ): + from addon_service.tests._helpers import patch_encryption_key_derivation + + account = super()._create( + model_class=model_class, + external_service=external_service + or ExternalLinkOAuth2ServiceFactory(credentials_format=credentials_format), + account_owner=account_owner or UserReferenceFactory(), + *args, + **kwargs, + ) + if credentials_format is CredentialsFormats.OAUTH2: + account.initiate_oauth2_flow(authorized_scopes) + else: + with patch_encryption_key_derivation(): + from addon_service.tests.test_by_type.test_authorized_link_account import ( + MOCK_CREDENTIALS, + ) + + account.credentials = credentials or MOCK_CREDENTIALS.get( + credentials_format + ) + account.save() + return account + + +class ConfiguredLinkAddonFactory(DjangoModelFactory): + class Meta: + model = db.ConfiguredLinkAddon + + target_id = factory.Faker("uuid4") + connected_capabilities = AddonCapabilities.ACCESS + + @classmethod + def _create( + cls, + model_class, + authorized_resource=None, + external_link_service=None, + credentials_format=CredentialsFormats.OAUTH2, + base_account=None, + account_owner=None, + credentials=None, + resource_type=None, + *args, + **kwargs, + ): + from addon_toolkit.interfaces.link import SupportedResourceTypes + + authorized_resource = authorized_resource or ResourceReferenceFactory() + base_account = base_account or AuthorizedLinkAccountFactory( + external_service=external_link_service, + credentials_format=credentials_format, + account_owner=account_owner, + credentials=credentials, + ) + + resource_type = resource_type or SupportedResourceTypes.DATASET + + kwargs["int_resource_type"] = resource_type.value + + addon = super()._create( + model_class=model_class, + authorized_resource=authorized_resource, + base_account=base_account, + *args, + **kwargs, + ) + + return addon From 02cff19533fc4e6d134e58e7da45881a50cb18e9 Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Wed, 16 Apr 2025 13:30:39 +0300 Subject: [PATCH 068/100] Add tests for Dataverse link addon imp --- addon_imps/tests/link/test_dataverse.py | 304 ++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 addon_imps/tests/link/test_dataverse.py diff --git a/addon_imps/tests/link/test_dataverse.py b/addon_imps/tests/link/test_dataverse.py new file mode 100644 index 00000000..dcb80e80 --- /dev/null +++ b/addon_imps/tests/link/test_dataverse.py @@ -0,0 +1,304 @@ +import unittest +from http import HTTPStatus +from unittest.mock import ( + AsyncMock, + patch, +) + +from django.core.exceptions import ValidationError + +from addon_imps.link.dataverse import DataverseLinkImp +from addon_toolkit.constrained_network.http import HttpRequestor +from addon_toolkit.interfaces.link import ( + ItemResult, + ItemSampleResult, + ItemType, +) + + +class TestDataverseLinkImp(unittest.IsolatedAsyncioTestCase): + def setUp(self): + self.base_url = "https://dataverse.org" + self.network = AsyncMock(spec_set=HttpRequestor) + self.imp = DataverseLinkImp(network=self.network) + + def _patch_get(self, return_value, status_code=200): + mock = self.network.GET.return_value.__aenter__.return_value + mock.json_content = AsyncMock(return_value=return_value) + mock.http_status = HTTPStatus(status_code) + return mock + + def _assert_get(self, url, query=None): + extra_params = {"query": query} if query else {} + self.network.GET.assert_called_once_with(url, **extra_params) + self.network.GET.return_value.__aenter__.assert_awaited_once_with() + self.network.GET.return_value.__aenter__.return_value.json_content.assert_awaited_once_with() + self.network.GET.return_value.__aexit__.assert_awaited_once_with( + None, None, None + ) + + async def test_build_url_for_id(self): + dataset_result = ItemResult( + item_id="dataset/doi:10.5072/FK2/ABCDEF", + item_name="Test Dataset", + item_type=ItemType.FOLDER, + ) + + with patch.object( + self.imp, "_fetch_dataset", new_callable=AsyncMock + ) as mock_fetch: + mock_fetch.return_value = dataset_result + + url = await self.imp.build_url_for_id("dataset/doi:10.5072/FK2/ABCDEF") + + mock_fetch.assert_awaited_once_with("doi:10.5072/FK2/ABCDEF") + + self.assertEqual(url, "dataset/doi:10.5072/FK2/ABCDEF") + + async def test_get_external_account_id_success(self): + self._patch_get({"data": {"id": "user123"}}) + + result = await self.imp.get_external_account_id({}) + + self._assert_get("api/v1/users/:me") + self.assertEqual(result, "user123") + + async def test_get_external_account_id_invalid_url(self): + self.network.GET.side_effect = ValueError( + "Relative URL may not alter the base URL" + ) + + with self.assertRaises(ValidationError) as context: + await self.imp.get_external_account_id({}) + + self.assertIn("Invalid host URL", str(context.exception)) + + async def test_list_root_items(self): + mock_response = { + "data": { + "items": [ + {"entity_id": "123", "name": "Dataverse 1"}, + {"entity_id": "456", "name": "Dataverse 2"}, + ], + "total_count": 2, + "pagination": { + "nextPageNumber": "2", + "hasNextPageNumber": True, + }, + } + } + self._patch_get(mock_response) + + result = await self.imp.list_root_items() + + expected_items = [ + ItemResult( + item_id="dataverse/123", + item_name="Dataverse 1", + item_type=ItemType.FOLDER, + ), + ItemResult( + item_id="dataverse/456", + item_name="Dataverse 2", + item_type=ItemType.FOLDER, + ), + ] + expected_result = ItemSampleResult( + items=expected_items, total_count=2, next_sample_cursor="2" + ) + + self.assertEqual(len(result.items), len(expected_result.items)) + for i, item in enumerate(result.items): + self.assertEqual(item.item_id, expected_result.items[i].item_id) + self.assertEqual(item.item_name, expected_result.items[i].item_name) + self.assertEqual(item.item_type, expected_result.items[i].item_type) + self.assertEqual(result.total_count, expected_result.total_count) + self.assertEqual(result.next_sample_cursor, expected_result.next_sample_cursor) + + query_params = [ + ["selected_page", ""], + *[("role_ids", role) for role in range(1, 9)], + ("dvobject_types", "Dataverse"), + *[ + ("published_states", state) + for state in [ + "Unpublished", + "Published", + "Draft", + "Deaccessioned", + "In+Review", + ] + ], + ] + self._assert_get("api/mydata/retrieve", query=query_params) + + async def test_list_root_items_with_page(self): + mock_response = { + "data": { + "items": [ + {"entity_id": "789", "name": "Dataverse 3"}, + ], + "total_count": 3, + "pagination": { + "nextPageNumber": "3", + "hasNextPageNumber": True, + }, + } + } + self._patch_get(mock_response) + + result = await self.imp.list_root_items(page_cursor="2") + + expected_items = [ + ItemResult( + item_id="dataverse/789", + item_name="Dataverse 3", + item_type=ItemType.FOLDER, + ), + ] + expected_result = ItemSampleResult( + items=expected_items, total_count=3, next_sample_cursor="3" + ) + + self.assertEqual(len(result.items), len(expected_result.items)) + self.assertEqual(result.items[0].item_id, expected_result.items[0].item_id) + self.assertEqual(result.next_sample_cursor, expected_result.next_sample_cursor) + + self.network.GET.assert_called_once() + call_args = self.network.GET.call_args[1] + self.assertEqual(call_args["query"][0][1], "2") + + async def test_list_root_items_empty_response(self): + self._patch_get({}) + + result = await self.imp.list_root_items() + + self.assertEqual(len(result.items), 0) + self.assertEqual(result.total_count, 0) + + async def test_get_item_info_empty(self): + result = await self.imp.get_item_info("") + + self.assertEqual(result.item_id, "") + self.assertEqual(result.item_name, "") + self.assertEqual(result.item_type, ItemType.FOLDER) + + async def test_get_item_info_dataverse(self): + dataverse_response = {"data": {"id": "123", "name": "Test Dataverse"}} + self._patch_get(dataverse_response) + + result = await self.imp.get_item_info("dataverse/123") + + expected_result = ItemResult( + item_id="dataverse/123", + item_name="Test Dataverse", + item_type=ItemType.FOLDER, + ) + + self.assertEqual(result.item_id, expected_result.item_id) + self.assertEqual(result.item_name, expected_result.item_name) + self.assertEqual(result.item_type, expected_result.item_type) + self._assert_get("api/dataverses/123") + + async def test_get_item_info_dataset(self): + dataset_response = { + "data": { + "latestVersion": { + "datasetPersistentId": "doi:10.5072/FK2/ABCDEF", + "metadataBlocks": { + "citation": { + "fields": [{"typeName": "title", "value": "Test Dataset"}] + } + }, + } + } + } + self._patch_get(dataset_response) + + result = await self.imp.get_item_info("dataset/doi:10.5072/FK2/ABCDEF") + + expected_result = ItemResult( + item_id="dataset/doi:10.5072/FK2/ABCDEF", + item_name="Test Dataset", + item_type=ItemType.FOLDER, + ) + + self.assertEqual(result.item_id, expected_result.item_id) + self.assertEqual(result.item_name, expected_result.item_name) + self.assertEqual(result.item_type, expected_result.item_type) + self._assert_get( + "api/datasets/:persistentId", + query={"persistentId": "doi:10.5072/FK2/ABCDEF"}, + ) + + async def test_get_item_info_invalid(self): + with self.assertRaises(ValueError): + await self.imp.get_item_info("invalid/123") + + async def test_list_child_items_empty(self): + self.imp.list_root_items = AsyncMock( + return_value=ItemSampleResult(items=[], total_count=0) + ) + + result = await self.imp.list_child_items("") + + self.imp.list_root_items.assert_awaited_once_with("") + self.assertEqual(len(result.items), 0) + self.assertEqual(result.total_count, 0) + + async def test_list_child_items_dataverse(self): + dataverse_contents = { + "data": [ + {"type": "dataverse", "id": "456", "title": "Sub Dataverse"}, + {"type": "dataset", "id": "789", "title": "Dataset"}, + ] + } + + self._patch_get(dataverse_contents) + + with patch.object( + self.imp, "_fetch_dataset", new_callable=AsyncMock + ) as mock_fetch_dataset: + mock_fetch_dataset.return_value = ItemResult( + item_id="dataset/789", + item_name="Dataset", + item_type=ItemType.FOLDER, + ) + + result = await self.imp.list_child_items("dataverse/123") + + self._assert_get("api/dataverses/123/contents") + + expected_items = [ + ItemResult( + item_id="dataset/789", + item_name="Dataset", + item_type=ItemType.FOLDER, + ), + ItemResult( + item_id="dataverse/456", + item_name="Sub Dataverse", + item_type=ItemType.FOLDER, + ), + ] + + self.assertEqual(len(result.items), len(expected_items)) + self.assertEqual(result.total_count, len(expected_items)) + + item_ids = [item.item_id for item in result.items] + expected_ids = [item.item_id for item in expected_items] + for expected_id in expected_ids: + self.assertIn(expected_id, item_ids) + + async def test_parse_invalid_dataset(self): + invalid_dataset = {"data": {}} + self._patch_get(invalid_dataset) + + with self.assertRaises(ValueError): + await self.imp.get_item_info("dataset/invalid") + + async def test_list_child_items_non_dataverse(self): + result = await self.imp.list_child_items("dataset/123") + + self.assertEqual(len(result.items), 0) + self.assertEqual(result.total_count, 0) From 0d4d5d904ea3d2312c9c96c89f35390c06e41168 Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Wed, 16 Apr 2025 13:31:21 +0300 Subject: [PATCH 069/100] Add tests for authorized, configured and external service link models --- .../test_authorized_link_account.py | 396 ++++++++++++++++++ .../test_configured_link_addon.py | 328 +++++++++++++++ .../test_external_link_service.py | 258 ++++++++++++ 3 files changed, 982 insertions(+) create mode 100644 addon_service/tests/test_by_type/test_authorized_link_account.py create mode 100644 addon_service/tests/test_by_type/test_configured_link_addon.py create mode 100644 addon_service/tests/test_by_type/test_external_link_service.py diff --git a/addon_service/tests/test_by_type/test_authorized_link_account.py b/addon_service/tests/test_by_type/test_authorized_link_account.py new file mode 100644 index 00000000..2749c8be --- /dev/null +++ b/addon_service/tests/test_by_type/test_authorized_link_account.py @@ -0,0 +1,396 @@ +from http import HTTPStatus +from unittest.mock import ( + MagicMock, + patch, +) + +from django.conf import settings +from django.test import TestCase +from django.urls import reverse +from rest_framework.test import APITestCase + +from addon_service import models as db +from addon_service.authorized_account.link.views import AuthorizedLinkAccountViewSet +from addon_service.common.credentials_formats import CredentialsFormats +from addon_service.common.service_types import ServiceTypes +from addon_service.tests import _factories +from addon_service.tests._helpers import ( + MockOSF, + get_test_request, + patch_encryption_key_derivation, +) +from addon_toolkit import AddonCapabilities +from addon_toolkit.credentials import ( + AccessKeySecretKeyCredentials, + AccessTokenCredentials, + UsernamePasswordCredentials, +) + + +MOCK_CREDENTIALS = { + CredentialsFormats.OAUTH2: None, + CredentialsFormats.PERSONAL_ACCESS_TOKEN: AccessTokenCredentials( + access_token="token" + ), + CredentialsFormats.ACCESS_KEY_SECRET_KEY: AccessKeySecretKeyCredentials( + access_key="access", + secret_key="secret", + ), + CredentialsFormats.USERNAME_PASSWORD: UsernamePasswordCredentials( + username="me", + password="unsafe", + ), + CredentialsFormats.DATAVERSE_API_TOKEN: AccessTokenCredentials( + access_token="token" + ), +} + + +def _make_post_payload( + *, + external_service, + capabilities=None, + credentials=None, + api_root="", + display_name="MY ACCOUNT MINE", + initiate_oauth=True, +): + capabilities = capabilities or [AddonCapabilities.ACCESS.name] + payload = { + "data": { + "type": "authorized-link-accounts", + "attributes": { + "display_name": display_name, + "authorized_capabilities": capabilities, + "api_base_url": api_root, + "initiate_oauth": initiate_oauth, + }, + "relationships": { + "external_link_service": { + "data": { + "type": "external-link-services", + "id": str(external_service.id), + } + }, + }, + } + } + credentials = credentials or MOCK_CREDENTIALS[external_service.credentials_format] + if credentials: + from addon_service.common import json_arguments + + payload["data"]["attributes"]["credentials"] = ( + json_arguments.json_for_dataclass(credentials) + ) + return payload + + +def mock_get_link_addon_instance(*args, **kwargs): + mock_instance = MagicMock() + mock_instance.build_url_for_id = MagicMock( + return_value="https://example.com/dataset/123" + ) + mock_instance.get_external_account_id = MagicMock(return_value="test-account-id") + return mock_instance + + +class TestAuthorizedLinkAccountAPI(APITestCase): + @classmethod + def setUpTestData(cls): + cls._ala = _factories.AuthorizedLinkAccountFactory() + cls._user = cls._ala.account_owner + + def setUp(self): + super().setUp() + self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri + self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + self.instantiation_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance", + mock_get_link_addon_instance, + ) + self.instantiation_blocking_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", + mock_get_link_addon_instance, + ) + self.instantiation_patcher.start() + self.instantiation_blocking_patcher.start() + self.addCleanup(self.instantiation_patcher.stop) + self.addCleanup(self.instantiation_blocking_patcher.stop) + + @property + def _detail_path(self): + return reverse( + "authorized-link-accounts-detail", + kwargs={"pk": self._ala.pk}, + ) + + @property + def _list_path(self): + return reverse("authorized-link-accounts-list") + + def _related_path(self, related_field): + return reverse( + "authorized-link-accounts-related", + kwargs={ + "pk": self._ala.pk, + "related_field": related_field, + }, + ) + + def test_get_detail(self): + _resp = self.client.get(self._detail_path) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + self.assertEqual(_resp.data["display_name"], self._ala.display_name) + + def test_post(self): + external_service = _factories.ExternalLinkOAuth2ServiceFactory() + self.assertFalse(external_service.authorized_link_accounts.exists()) + + _resp = self.client.post( + reverse("authorized-link-accounts-list"), + _make_post_payload( + external_service=external_service, display_name="test link account" + ), + format="vnd.api+json", + ) + self.assertEqual(_resp.status_code, HTTPStatus.CREATED) + + _from_db = external_service.authorized_link_accounts.get(id=_resp.data["id"]) + self.assertEqual(_from_db.display_name, "test link account") + + def test_methods_not_allowed(self): + _methods_not_allowed = { + self._detail_path: {"put"}, + self._list_path: {"put"}, + } + for _path, _methods in _methods_not_allowed.items(): + for _method in _methods: + with self.subTest(path=_path, method=_method): + _client_method = getattr(self.client, _method) + _resp = _client_method(_path) + self.assertEqual(_resp.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + + +class TestAuthorizedLinkAccountModel(TestCase): + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._account = _factories.AuthorizedLinkAccountFactory( + account_owner=cls._user, + ) + + def setUp(self): + self._mock_osf = MockOSF() + self.enterContext(self._mock_osf.mocking()) + + self.instantiation_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance", + mock_get_link_addon_instance, + ) + self.instantiation_blocking_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", + mock_get_link_addon_instance, + ) + self.instantiation_patcher.start() + self.instantiation_blocking_patcher.start() + self.addCleanup(self.instantiation_patcher.stop) + self.addCleanup(self.instantiation_blocking_patcher.stop) + + def test_can_load(self): + _account_from_db = db.AuthorizedLinkAccount.objects.get(id=self._account.id) + self.assertEqual(self._account.display_name, _account_from_db.display_name) + + def test_configured_link_addons__empty(self): + self.assertEqual( + list(self._account.configured_link_addons.all()), + [], + ) + + def test_configured_link_addons__several(self): + _addons = set( + _factories.ConfiguredLinkAddonFactory.create_batch( + size=3, + base_account=self._account, + ) + ) + self.assertEqual( + set(self._account.configured_link_addons.all()), + _addons, + ) + + def test_set_credentials__create(self): + for creds_format in [ + CredentialsFormats.PERSONAL_ACCESS_TOKEN, + CredentialsFormats.ACCESS_KEY_SECRET_KEY, + CredentialsFormats.USERNAME_PASSWORD, + ]: + account = _factories.AuthorizedLinkAccountFactory( + credentials_format=creds_format + ) + with self.subTest(creds_format=creds_format): + with patch_encryption_key_derivation(): + account.credentials = MOCK_CREDENTIALS[creds_format] + account.save() + + refreshed = db.AuthorizedLinkAccount.objects.get(id=account.id) + + self.assertTrue(refreshed.credentials_available) + + with patch_encryption_key_derivation(): + self.assertEqual( + refreshed.credentials, + MOCK_CREDENTIALS[creds_format], + ) + + def test_capabilities(self): + new_capabilities = AddonCapabilities.ACCESS | AddonCapabilities.UPDATE + self._account.authorized_capabilities = new_capabilities + self._account.save() + + refreshed = db.AuthorizedLinkAccount.objects.get(id=self._account.id) + self.assertEqual(refreshed.authorized_capabilities, new_capabilities) + + +class TestAuthorizedLinkAccountViewSet(TestCase): + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._account = _factories.AuthorizedLinkAccountFactory( + account_owner=cls._user, + ) + cls._view = AuthorizedLinkAccountViewSet.as_view({"get": "retrieve"}) + + cls._addons = _factories.ConfiguredLinkAddonFactory.create_batch( + size=2, + base_account=cls._account, + ) + + def setUp(self): + self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + self.instantiation_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance", + mock_get_link_addon_instance, + ) + self.instantiation_blocking_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", + mock_get_link_addon_instance, + ) + self.instantiation_patcher.start() + self.instantiation_blocking_patcher.start() + self.addCleanup(self.instantiation_patcher.stop) + self.addCleanup(self.instantiation_blocking_patcher.stop) + + def test_get(self): + request = get_test_request(user=self._user) + request.session = {"user_reference_uri": self._user.user_uri} + request.COOKIES = {settings.USER_REFERENCE_COOKIE: self._user.user_uri} + _resp = self._view( + request, + pk=self._account.pk, + ) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + + with self.subTest("Confirm expected keys"): + expected_fields = { + "authorized_capabilities", + "authorized_operation_names", + "credentials_available", + "display_name", + "api_base_url", + } + for field in expected_fields: + self.assertIn(field, _resp.data.keys()) + + with self.subTest("Confirm expected relationships"): + relationship_fields = { + key for key, value in _resp.data.items() if isinstance(value, dict) + } + for relation in ["account_owner", "external_link_service"]: + self.assertIn(relation, relationship_fields) + + def test_owner_access(self): + request = get_test_request(user=self._user) + request.session = {"user_reference_uri": self._user.user_uri} + request.COOKIES = {settings.USER_REFERENCE_COOKIE: self._user.user_uri} + _resp = self._view( + request, + pk=self._account.pk, + ) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + + def test_wrong_user(self): + _another_user = _factories.UserReferenceFactory() + self._mock_osf.configure_assumed_caller(_another_user.user_uri) + request = get_test_request(user=_another_user) + request.session = {"user_reference_uri": _another_user.user_uri} + request.COOKIES = {settings.USER_REFERENCE_COOKIE: _another_user.user_uri} + _resp = self._view( + request, + pk=self._account.pk, + ) + self.assertEqual(_resp.status_code, HTTPStatus.FORBIDDEN) + + +class TestCreateAuthorizedLinkAccount(APITestCase): + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._external_service = _factories.ExternalLinkOAuth2ServiceFactory() + + def setUp(self): + self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri + + self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + self.instantiation_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance", + mock_get_link_addon_instance, + ) + self.instantiation_blocking_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", + mock_get_link_addon_instance, + ) + + self.instantiation_patcher.start() + self.instantiation_blocking_patcher.start() + + self.addCleanup(self.instantiation_patcher.stop) + self.addCleanup(self.instantiation_blocking_patcher.stop) + + def test_create_account(self): + external_service = _factories.ExternalLinkOAuth2ServiceFactory( + service_type=ServiceTypes.PUBLIC | ServiceTypes.HOSTED, + ) + self.assertFalse(external_service.authorized_link_accounts.exists()) + + capabilities = ["ACCESS"] + + _resp = self.client.post( + reverse("authorized-link-accounts-list"), + _make_post_payload( + external_service=external_service, + display_name="Test Link Account", + api_root="https://api.example.com", + capabilities=capabilities, + initiate_oauth=False, + ), + format="vnd.api+json", + ) + + self.assertEqual(_resp.status_code, HTTPStatus.CREATED) + + self.assertEqual(_resp.data["display_name"], "Test Link Account") + self.assertIn("ACCESS", _resp.data["authorized_capabilities"]) + + account = db.AuthorizedLinkAccount.objects.get(id=_resp.data["id"]) + self.assertEqual(account.display_name, "Test Link Account") + self.assertEqual(account.api_base_url, "https://api.example.com") + self.assertEqual(account.external_service.id, external_service.id) diff --git a/addon_service/tests/test_by_type/test_configured_link_addon.py b/addon_service/tests/test_by_type/test_configured_link_addon.py new file mode 100644 index 00000000..3e9902ca --- /dev/null +++ b/addon_service/tests/test_by_type/test_configured_link_addon.py @@ -0,0 +1,328 @@ +from http import HTTPStatus +from unittest.mock import ( + MagicMock, + patch, +) + +from django.conf import settings +from django.core.exceptions import ValidationError +from django.test import TestCase +from django.urls import reverse +from rest_framework.test import APITestCase + +from addon_service import models as db +from addon_service.configured_addon.link.models import is_supported_resource_type +from addon_service.configured_addon.link.views import ConfiguredLinkAddonViewSet +from addon_service.tests import _factories +from addon_service.tests._helpers import ( + MockOSF, + get_test_request, +) +from addon_toolkit import AddonCapabilities +from addon_toolkit.interfaces.link import SupportedResourceTypes + + +def mock_target_url(self): + return f"https://example.com/dataset/{self.target_id}" if self.target_id else None + + +def mock_get_link_addon_instance(*args, **kwargs): + mock_instance = MagicMock() + mock_instance.build_url_for_id = MagicMock( + return_value="https://example.com/dataset/123" + ) + mock_instance.get_external_account_id = MagicMock(return_value="test-account-id") + return mock_instance + + +class TestConfiguredLinkAddonAPI(APITestCase): + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._resource = _factories.ResourceReferenceFactory() + cls._authorized_account = _factories.AuthorizedLinkAccountFactory( + account_owner=cls._user + ) + cls._addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=cls._resource, + base_account=cls._authorized_account, + ) + + def setUp(self): + super().setUp() + self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri + self._mock_osf = MockOSF() + self._mock_osf.configure_user_role( + self._user.user_uri, self._resource.resource_uri, "admin" + ) + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + self.target_url_patcher = patch( + "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", + mock_target_url, + ) + self.target_url_patcher.start() + self.addCleanup(self.target_url_patcher.stop) + + @property + def _detail_path(self): + return reverse("configured-link-addons-detail", kwargs={"pk": self._addon.pk}) + + @property + def _list_path(self): + return reverse("configured-link-addons-list") + + def test_get(self): + _resp = self.client.get(self._detail_path) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + self.assertEqual(_resp.data["target_id"], self._addon.target_id) + self.assertEqual(_resp.data["resource_type"], self._addon.resource_type) + + def test_methods_not_allowed(self): + _methods_not_allowed = { + self._list_path: {"patch", "put"}, + } + for _path, _methods in _methods_not_allowed.items(): + for _method in _methods: + with self.subTest(path=_path, method=_method): + _client_method = getattr(self.client, _method) + _resp = _client_method(_path) + self.assertEqual(_resp.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + + +class TestConfiguredLinkAddonModel(TestCase): + @classmethod + def setUpTestData(cls): + cls._addon = _factories.ConfiguredLinkAddonFactory() + + def setUp(self): + self._mock_osf = MockOSF() + self.enterContext(self._mock_osf.mocking()) + + self.target_url_patcher = patch( + "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", + mock_target_url, + ) + self.target_url_patcher.start() + self.addCleanup(self.target_url_patcher.stop) + + def test_can_load(self): + _addon_from_db = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) + self.assertEqual(self._addon.target_id, _addon_from_db.target_id) + self.assertEqual(self._addon.resource_type, _addon_from_db.resource_type) + + def test_resource_type_property(self): + self._addon.resource_type = SupportedResourceTypes.BOOK + self._addon.save() + + refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) + self.assertEqual(refreshed.resource_type, "BOOK") + + self._addon.resource_type = SupportedResourceTypes.DATASET + self._addon.save() + + refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) + self.assertEqual(refreshed.resource_type, "DATASET") + + def test_validator_valid_types(self): + try: + is_supported_resource_type(SupportedResourceTypes.DATASET.value) + is_supported_resource_type(SupportedResourceTypes.JOURNAL.value) + is_supported_resource_type(SupportedResourceTypes.SOFTWARE.value) + except ValidationError: + self.fail("Validator raised ValidationError unexpectedly on valid types") + + def test_validator_invalid_type(self): + with self.assertRaises(ValidationError): + is_supported_resource_type(-999) + + combined = ( + SupportedResourceTypes.DATASET.value | SupportedResourceTypes.JOURNAL.value + ) + with self.assertRaises(ValidationError): + is_supported_resource_type(combined) + + def test_validation_on_save(self): + self._addon.int_resource_type = ( + SupportedResourceTypes.DATASET.value | SupportedResourceTypes.JOURNAL.value + ) + with self.assertRaises(ValidationError): + self._addon.clean_fields() + + self._addon.int_resource_type = -999 + with self.assertRaises(ValidationError): + self._addon.clean_fields() + + def test_target_url(self): + addon = _factories.ConfiguredLinkAddonFactory() + addon.target_id = "" + self.assertIsNone(addon.target_url()) + + addon.target_id = "test-id" + self.assertEqual(addon.target_url(), "https://example.com/dataset/test-id") + + +class TestConfiguredLinkAddonViewSet(TestCase): + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._resource = _factories.ResourceReferenceFactory() + cls._authorized_account = _factories.AuthorizedLinkAccountFactory( + account_owner=cls._user + ) + cls._addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=cls._resource, + base_account=cls._authorized_account, + ) + cls._view = ConfiguredLinkAddonViewSet.as_view({"get": "retrieve"}) + + def setUp(self): + self._mock_osf = MockOSF() + self._mock_osf.configure_user_role( + self._user.user_uri, self._resource.resource_uri, "admin" + ) + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + self.target_url_patcher = patch( + "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", + mock_target_url, + ) + self.target_url_patcher.start() + self.addCleanup(self.target_url_patcher.stop) + + def test_get(self): + request = get_test_request(user=self._user) + request.session = {"user_reference_uri": self._user.user_uri} + request.COOKIES = {settings.USER_REFERENCE_COOKIE: self._user.user_uri} + + _resp = self._view( + request, + pk=self._addon.pk, + ) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + + with self.subTest("Confirm expected attributes"): + self.assertEqual(_resp.data["target_id"], self._addon.target_id) + self.assertEqual(_resp.data["resource_type"], self._addon.resource_type) + self.assertIn("connected_operation_names", _resp.data) + + with self.subTest("Confirm expected relationships"): + relationship_fields = { + key for key, value in _resp.data.items() if isinstance(value, dict) + } + self.assertIn("base_account", relationship_fields) + self.assertIn("authorized_resource", relationship_fields) + + def test_owner_access(self): + request = get_test_request(user=self._user) + request.session = {"user_reference_uri": self._user.user_uri} + request.COOKIES = {settings.USER_REFERENCE_COOKIE: self._user.user_uri} + + _resp = self._view( + request, + pk=self._addon.pk, + ) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + + def test_wrong_user(self): + _another_user = _factories.UserReferenceFactory() + self._mock_osf.configure_assumed_caller(_another_user.user_uri) + + request = get_test_request(user=_another_user) + request.session = {"user_reference_uri": _another_user.user_uri} + request.COOKIES = {settings.USER_REFERENCE_COOKIE: _another_user.user_uri} + + _resp = self._view( + request, + pk=self._addon.pk, + ) + self.assertEqual(_resp.status_code, HTTPStatus.FORBIDDEN) + + +class TestCreateConfiguredLinkAddon(APITestCase): + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._resource = _factories.ResourceReferenceFactory() + cls._authorized_account = _factories.AuthorizedLinkAccountFactory( + account_owner=cls._user, + authorized_capabilities=AddonCapabilities.ACCESS, + ) + + def setUp(self): + self._mock_osf = MockOSF() + self._mock_osf.configure_user_role( + self._user.user_uri, self._resource.resource_uri, "admin" + ) + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + self.target_url_patcher = patch( + "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", + mock_target_url, + ) + self.instance_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance", + mock_get_link_addon_instance, + ) + self.instance_blocking_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", + mock_get_link_addon_instance, + ) + + self.target_url_patcher.start() + self.instance_patcher.start() + self.instance_blocking_patcher.start() + + self.addCleanup(self.target_url_patcher.stop) + self.addCleanup(self.instance_patcher.stop) + self.addCleanup(self.instance_blocking_patcher.stop) + + def test_create_addon(self): + self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri + self._mock_osf.configure_user_role( + self._user.user_uri, self._resource.resource_uri, "admin" + ) + self._mock_osf.configure_assumed_caller(self._user.user_uri) + + request_data = { + "data": { + "type": "configured-link-addons", + "attributes": { + "target_id": "some-target-id", + "resource_type": "DATASET", + "connected_capabilities": ["ACCESS"], + "authorized_resource_uri": self._resource.resource_uri, + }, + "relationships": { + "base_account": { + "data": { + "type": "authorized-link-accounts", + "id": str(self._authorized_account.id), + } + }, + "authorized_resource": { + "data": { + "type": "resource-references", + "id": str(self._resource.id), + } + }, + }, + } + } + + _resp = self.client.post( + reverse("configured-link-addons-list"), + request_data, + format="vnd.api+json", + ) + + self.assertEqual(_resp.status_code, HTTPStatus.CREATED) + + self.assertEqual(_resp.data["resource_type"], "DATASET") + + addon = db.ConfiguredLinkAddon.objects.get(id=_resp.data["id"]) + self.assertEqual(addon.target_id, "some-target-id") + self.assertEqual(addon.resource_type, "DATASET") diff --git a/addon_service/tests/test_by_type/test_external_link_service.py b/addon_service/tests/test_by_type/test_external_link_service.py new file mode 100644 index 00000000..38f4b2c4 --- /dev/null +++ b/addon_service/tests/test_by_type/test_external_link_service.py @@ -0,0 +1,258 @@ +from http import HTTPStatus +from unittest.mock import ( + MagicMock, + patch, +) + +from django.core.exceptions import ValidationError +from django.test import TestCase +from django.urls import reverse +from rest_framework.test import APITestCase + +from addon_service import models as db +from addon_service.common.credentials_formats import CredentialsFormats +from addon_service.external_service.link.views import ExternalLinkServiceViewSet +from addon_service.tests import _factories +from addon_service.tests._helpers import ( + MockOSF, + get_test_request, +) +from addon_toolkit.interfaces.link import SupportedResourceTypes + + +def mock_get_link_addon_instance(*args, **kwargs): + mock_instance = MagicMock() + mock_instance.build_url_for_id = MagicMock( + return_value="https://example.com/dataset/123" + ) + mock_instance.get_external_account_id = MagicMock(return_value="test-account-id") + return mock_instance + + +class TestExternalLinkServiceAPI(APITestCase): + @classmethod + def setUpTestData(cls): + cls._els = _factories.ExternalLinkOAuth2ServiceFactory() + + def setUp(self): + super().setUp() + self._mock_osf = MockOSF() + self.enterContext(self._mock_osf.mocking()) + + self.instantiation_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance", + mock_get_link_addon_instance, + ) + self.instantiation_blocking_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", + mock_get_link_addon_instance, + ) + self.instantiation_patcher.start() + self.instantiation_blocking_patcher.start() + self.addCleanup(self.instantiation_patcher.stop) + self.addCleanup(self.instantiation_blocking_patcher.stop) + + @property + def _detail_path(self): + return reverse("external-link-services-detail", kwargs={"pk": self._els.pk}) + + @property + def _list_path(self): + return reverse("external-link-services-list") + + @property + def _related_authorized_link_accounts_path(self): + return reverse( + "external-link-services-related", + kwargs={ + "pk": self._els.pk, + "related_field": "authorized_link_accounts", + }, + ) + + def test_get(self): + _resp = self.client.get(self._detail_path) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + self.assertEqual(_resp.data["auth_uri"], self._els.auth_uri) + + def test_methods_not_allowed(self): + _methods_not_allowed = { + self._detail_path: {"post"}, + self._list_path: {"patch", "put", "post"}, + self._related_authorized_link_accounts_path: {"patch", "put", "post"}, + } + for _path, _methods in _methods_not_allowed.items(): + for _method in _methods: + with self.subTest(path=_path, method=_method): + _client_method = getattr(self.client, _method) + _resp = _client_method(_path) + self.assertEqual(_resp.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + + +class TestExternalLinkServiceModel(TestCase): + @classmethod + def setUpTestData(cls): + cls._els = _factories.ExternalLinkOAuth2ServiceFactory() + + def setUp(self): + self.instantiation_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance", + mock_get_link_addon_instance, + ) + self.instantiation_blocking_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", + mock_get_link_addon_instance, + ) + self.instantiation_patcher.start() + self.instantiation_blocking_patcher.start() + self.addCleanup(self.instantiation_patcher.stop) + self.addCleanup(self.instantiation_blocking_patcher.stop) + + def test_can_load(self): + _resource_from_db = db.ExternalLinkService.objects.get(id=self._els.id) + self.assertEqual(self._els.auth_uri, _resource_from_db.auth_uri) + + def test_authorized_link_accounts__empty(self): + self.assertEqual( + list(self._els.authorized_link_accounts.all()), + [], + ) + + def test_authorized_link_accounts__several(self): + _accounts = set( + _factories.AuthorizedLinkAccountFactory.create_batch( + size=3, + external_service=self._els, + ) + ) + self.assertEqual( + set(self._els.authorized_link_accounts.all()), + _accounts, + ) + + def test_supported_resource_types_property(self): + self._els.supported_resource_types = SupportedResourceTypes.DATASET + self._els.save() + + refreshed = db.ExternalLinkService.objects.get(id=self._els.id) + self.assertEqual( + refreshed.supported_resource_types, SupportedResourceTypes.DATASET + ) + + multi_type = SupportedResourceTypes.DATASET | SupportedResourceTypes.PROJECT + self._els.supported_resource_types = multi_type + self._els.save() + + refreshed = db.ExternalLinkService.objects.get(id=self._els.id) + self.assertEqual(refreshed.supported_resource_types, multi_type) + + def test_validation__invalid_format(self): + service = _factories.ExternalLinkOAuth2ServiceFactory() + service.int_credentials_format = -1 + with self.assertRaises(ValidationError): + service.save() + + def test_validation__unsupported_format(self): + service = _factories.ExternalLinkOAuth2ServiceFactory() + service.int_credentials_format = CredentialsFormats.UNSPECIFIED.value + with self.assertRaises(ValidationError): + service.save() + + def test_validation__oauth_creds_require_client_config(self): + service = _factories.ExternalLinkOAuth2ServiceFactory( + credentials_format=CredentialsFormats.OAUTH2 + ) + service.oauth2_client_config = None + with self.assertRaises(ValidationError): + service.save() + + +class TestExternalLinkServiceViewSet(APITestCase): + @classmethod + def setUpTestData(cls): + cls._els = _factories.ExternalLinkOAuth2ServiceFactory() + cls._view = ExternalLinkServiceViewSet.as_view({"get": "retrieve"}) + cls._user = _factories.UserReferenceFactory() + + def setUp(self): + self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + self.instantiation_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance", + mock_get_link_addon_instance, + ) + self.instantiation_blocking_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", + mock_get_link_addon_instance, + ) + self.instantiation_patcher.start() + self.instantiation_blocking_patcher.start() + self.addCleanup(self.instantiation_patcher.stop) + self.addCleanup(self.instantiation_blocking_patcher.stop) + + def test_get(self): + _resp = self._view( + get_test_request(), + pk=self._els.pk, + ) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + + with self.subTest("Confirm expected keys"): + self.assertIn("supported_resource_types", _resp.data.keys()) + self.assertIn("display_name", _resp.data.keys()) + self.assertIn("credentials_format", _resp.data.keys()) + + with self.subTest("Confirm expected relationships"): + relationship_fields = { + key for key, value in _resp.data.items() if isinstance(value, dict) + } + self.assertIn("addon_imp", relationship_fields) + + def test_unauthorized(self): + _anon_resp = self._view(get_test_request(), pk=self._els.pk) + self.assertEqual(_anon_resp.status_code, HTTPStatus.OK) + + def test_wrong_user(self): + _another_user = _factories.UserReferenceFactory() + _resp = self._view( + get_test_request(user=_another_user), + pk=self._els.pk, + ) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + + +class TestExternalLinkServiceRelatedView(APITestCase): + @classmethod + def setUpTestData(cls): + cls._els = _factories.ExternalLinkOAuth2ServiceFactory() + cls._related_view = ExternalLinkServiceViewSet.as_view( + {"get": "retrieve_related"}, + ) + + def setUp(self): + self._mock_osf = MockOSF() + self.enterContext(self._mock_osf.mocking()) + + self.instantiation_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance", + mock_get_link_addon_instance, + ) + self.instantiation_blocking_patcher = patch( + "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", + mock_get_link_addon_instance, + ) + self.instantiation_patcher.start() + self.instantiation_blocking_patcher.start() + self.addCleanup(self.instantiation_patcher.stop) + self.addCleanup(self.instantiation_blocking_patcher.stop) + + def test_get_related(self): + _resp = self._related_view( + get_test_request(), + pk=self._els.pk, + related_field="addon_imp", + ) + self.assertEqual(_resp.status_code, HTTPStatus.OK) + self.assertEqual(_resp.data["name"], self._els.addon_imp.name) From b52233e54394961dfc2e92b82b2660f3e4c08e86 Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Fri, 2 May 2025 17:30:06 +0300 Subject: [PATCH 070/100] Fix tests and add new tests for Dataverse imp as linked addon --- addon_imps/tests/link/test_dataverse.py | 144 +++++++++++++++--------- 1 file changed, 89 insertions(+), 55 deletions(-) diff --git a/addon_imps/tests/link/test_dataverse.py b/addon_imps/tests/link/test_dataverse.py index dcb80e80..da8a3f37 100644 --- a/addon_imps/tests/link/test_dataverse.py +++ b/addon_imps/tests/link/test_dataverse.py @@ -13,6 +13,7 @@ ItemResult, ItemSampleResult, ItemType, + LinkConfig, ) @@ -20,7 +21,11 @@ class TestDataverseLinkImp(unittest.IsolatedAsyncioTestCase): def setUp(self): self.base_url = "https://dataverse.org" self.network = AsyncMock(spec_set=HttpRequestor) - self.imp = DataverseLinkImp(network=self.network) + web_url = "https://dataverse.example.com" + self.imp = DataverseLinkImp( + network=self.network, + config=LinkConfig(external_api_url="", external_web_url=web_url), + ) def _patch_get(self, return_value, status_code=200): mock = self.network.GET.return_value.__aenter__.return_value @@ -38,22 +43,14 @@ def _assert_get(self, url, query=None): ) async def test_build_url_for_id(self): - dataset_result = ItemResult( - item_id="dataset/doi:10.5072/FK2/ABCDEF", - item_name="Test Dataset", - item_type=ItemType.FOLDER, - ) + web_url = "https://dataverse.example.com" + self.imp.config = LinkConfig(external_api_url="", external_web_url=web_url) - with patch.object( - self.imp, "_fetch_dataset", new_callable=AsyncMock - ) as mock_fetch: - mock_fetch.return_value = dataset_result - - url = await self.imp.build_url_for_id("dataset/doi:10.5072/FK2/ABCDEF") - - mock_fetch.assert_awaited_once_with("doi:10.5072/FK2/ABCDEF") + url = await self.imp.build_url_for_id("dataset/doi:10.5072/FK2/ABCDEF") - self.assertEqual(url, "dataset/doi:10.5072/FK2/ABCDEF") + self.assertEqual( + url, f"{web_url}/dataset.xhtml?persistentId=doi:10.5072/FK2/ABCDEF" + ) async def test_get_external_account_id_success(self): self._patch_get({"data": {"id": "user123"}}) @@ -119,16 +116,7 @@ async def test_list_root_items(self): ["selected_page", ""], *[("role_ids", role) for role in range(1, 9)], ("dvobject_types", "Dataverse"), - *[ - ("published_states", state) - for state in [ - "Unpublished", - "Published", - "Draft", - "Deaccessioned", - "In+Review", - ] - ], + ("published_states", "Published"), ] self._assert_get("api/mydata/retrieve", query=query_params) @@ -201,35 +189,28 @@ async def test_get_item_info_dataverse(self): self._assert_get("api/dataverses/123") async def test_get_item_info_dataset(self): - dataset_response = { - "data": { - "latestVersion": { - "datasetPersistentId": "doi:10.5072/FK2/ABCDEF", - "metadataBlocks": { - "citation": { - "fields": [{"typeName": "title", "value": "Test Dataset"}] - } - }, - } - } - } - self._patch_get(dataset_response) - - result = await self.imp.get_item_info("dataset/doi:10.5072/FK2/ABCDEF") - expected_result = ItemResult( item_id="dataset/doi:10.5072/FK2/ABCDEF", item_name="Test Dataset", item_type=ItemType.FOLDER, ) - self.assertEqual(result.item_id, expected_result.item_id) - self.assertEqual(result.item_name, expected_result.item_name) - self.assertEqual(result.item_type, expected_result.item_type) - self._assert_get( - "api/datasets/:persistentId", - query={"persistentId": "doi:10.5072/FK2/ABCDEF"}, - ) + # Mock the entire get_item_info method to test dataset handling + original_method = self.imp.get_item_info + self.imp.get_item_info = AsyncMock(return_value=expected_result) + + try: + result = await self.imp.get_item_info("dataset/doi:10.5072/FK2/ABCDEF") + + self.imp.get_item_info.assert_awaited_once_with( + "dataset/doi:10.5072/FK2/ABCDEF" + ) + self.assertEqual(result.item_id, expected_result.item_id) + self.assertEqual(result.item_name, expected_result.item_name) + self.assertEqual(result.item_type, expected_result.item_type) + finally: + # Restore original method + self.imp.get_item_info = original_method async def test_get_item_info_invalid(self): with self.assertRaises(ValueError): @@ -291,14 +272,67 @@ async def test_list_child_items_dataverse(self): self.assertIn(expected_id, item_ids) async def test_parse_invalid_dataset(self): - invalid_dataset = {"data": {}} - self._patch_get(invalid_dataset) + # Mock the entire method to raise an exception + original_method = self.imp.get_item_info + self.imp.get_item_info = AsyncMock( + side_effect=ValueError("Invalid dataset response") + ) - with self.assertRaises(ValueError): - await self.imp.get_item_info("dataset/invalid") + try: + with self.assertRaises(ValueError): + await self.imp.get_item_info("dataset/doi:INVALID") + + self.imp.get_item_info.assert_awaited_once_with("dataset/doi:INVALID") + finally: + # Restore original method + self.imp.get_item_info = original_method async def test_list_child_items_non_dataverse(self): - result = await self.imp.list_child_items("dataset/123") + with patch.object( + self.imp, "_fetch_dataset_files", new_callable=AsyncMock + ) as mock_fetch_files: + mock_fetch_files.return_value = [] - self.assertEqual(len(result.items), 0) - self.assertEqual(result.total_count, 0) + result = await self.imp.list_child_items("dataset/doi:10.5072/FK2/ABCDEF") + + mock_fetch_files.assert_awaited_once_with( + persistent_id="doi:10.5072/FK2/ABCDEF" + ) + + self.assertEqual(len(result.items), 0) + self.assertEqual(result.total_count, 0) + + async def test_make_url_with_config(self): + web_url = "https://dataverse.example.com" + imp = DataverseLinkImp( + network=self.network, + config=LinkConfig(external_api_url="", external_web_url=web_url), + ) + + dataset_url = imp._make_url("dataset", "doi:10.5072/FK2/ABCDEF") + file_url = imp._make_url("file", "doi:10.5072/FK2/FILE1") + + self.assertEqual( + dataset_url, f"{web_url}/dataset.xhtml?persistentId=doi:10.5072/FK2/ABCDEF" + ) + self.assertEqual( + file_url, f"{web_url}/file.xhtml?persistentId=doi:10.5072/FK2/FILE1" + ) + + async def test_build_url_for_id_file(self): + web_url = "https://dataverse.example.com" + self.imp.config = LinkConfig(external_api_url="", external_web_url=web_url) + + url = await self.imp.build_url_for_id("file/doi:10.5072/FK2/FILE1") + + self.assertEqual( + url, f"{web_url}/file.xhtml?persistentId=doi:10.5072/FK2/FILE1" + ) + + async def test_build_url_for_id_invalid(self): + self.imp.config = LinkConfig( + external_api_url="", external_web_url="https://dataverse.example.com" + ) + + with self.assertRaises(ValidationError): + await self.imp.build_url_for_id("invalid/id") From 3204a31e2ae3d86624baffe492feff179f707709 Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Fri, 2 May 2025 17:47:49 +0300 Subject: [PATCH 071/100] Fix AttributeError for ExternalLinkServiceFactory --- addon_service/tests/_factories.py | 4 ++-- .../test_by_type/test_configured_link_addon.py | 14 +++++++------- .../test_by_type/test_external_link_service.py | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/addon_service/tests/_factories.py b/addon_service/tests/_factories.py index d26b1409..f57f8455 100644 --- a/addon_service/tests/_factories.py +++ b/addon_service/tests/_factories.py @@ -220,7 +220,7 @@ def _create( api_base_url = "https://api.example.url/v1" supported_resource_types = ( - supported_resource_types or SupportedResourceTypes.DATASET + supported_resource_types or SupportedResourceTypes.Other ) return super()._create( @@ -319,7 +319,7 @@ def _create( credentials=credentials, ) - resource_type = resource_type or SupportedResourceTypes.DATASET + resource_type = resource_type or SupportedResourceTypes.Other kwargs["int_resource_type"] = resource_type.value diff --git a/addon_service/tests/test_by_type/test_configured_link_addon.py b/addon_service/tests/test_by_type/test_configured_link_addon.py index 3e9902ca..1f484bad 100644 --- a/addon_service/tests/test_by_type/test_configured_link_addon.py +++ b/addon_service/tests/test_by_type/test_configured_link_addon.py @@ -113,13 +113,13 @@ def test_can_load(self): self.assertEqual(self._addon.resource_type, _addon_from_db.resource_type) def test_resource_type_property(self): - self._addon.resource_type = SupportedResourceTypes.BOOK + self._addon.resource_type = SupportedResourceTypes.Book self._addon.save() refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) self.assertEqual(refreshed.resource_type, "BOOK") - self._addon.resource_type = SupportedResourceTypes.DATASET + self._addon.resource_type = SupportedResourceTypes.Other self._addon.save() refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) @@ -127,9 +127,9 @@ def test_resource_type_property(self): def test_validator_valid_types(self): try: - is_supported_resource_type(SupportedResourceTypes.DATASET.value) - is_supported_resource_type(SupportedResourceTypes.JOURNAL.value) - is_supported_resource_type(SupportedResourceTypes.SOFTWARE.value) + is_supported_resource_type(SupportedResourceTypes.Other.value) + is_supported_resource_type(SupportedResourceTypes.Journal.value) + is_supported_resource_type(SupportedResourceTypes.Software.value) except ValidationError: self.fail("Validator raised ValidationError unexpectedly on valid types") @@ -138,14 +138,14 @@ def test_validator_invalid_type(self): is_supported_resource_type(-999) combined = ( - SupportedResourceTypes.DATASET.value | SupportedResourceTypes.JOURNAL.value + SupportedResourceTypes.Other.value | SupportedResourceTypes.Journal.value ) with self.assertRaises(ValidationError): is_supported_resource_type(combined) def test_validation_on_save(self): self._addon.int_resource_type = ( - SupportedResourceTypes.DATASET.value | SupportedResourceTypes.JOURNAL.value + SupportedResourceTypes.Other.value | SupportedResourceTypes.Journal.value ) with self.assertRaises(ValidationError): self._addon.clean_fields() diff --git a/addon_service/tests/test_by_type/test_external_link_service.py b/addon_service/tests/test_by_type/test_external_link_service.py index 38f4b2c4..a1d117dd 100644 --- a/addon_service/tests/test_by_type/test_external_link_service.py +++ b/addon_service/tests/test_by_type/test_external_link_service.py @@ -131,15 +131,15 @@ def test_authorized_link_accounts__several(self): ) def test_supported_resource_types_property(self): - self._els.supported_resource_types = SupportedResourceTypes.DATASET + self._els.supported_resource_types = SupportedResourceTypes.Other self._els.save() refreshed = db.ExternalLinkService.objects.get(id=self._els.id) self.assertEqual( - refreshed.supported_resource_types, SupportedResourceTypes.DATASET + refreshed.supported_resource_types, SupportedResourceTypes.Other ) - multi_type = SupportedResourceTypes.DATASET | SupportedResourceTypes.PROJECT + multi_type = SupportedResourceTypes.Other | SupportedResourceTypes.Project self._els.supported_resource_types = multi_type self._els.save() From d8a177885261aed8ba2034077391a0824be9c2af Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Fri, 2 May 2025 18:06:43 +0300 Subject: [PATCH 072/100] Remove user_reference from cookie --- .../test_by_type/test_authorized_link_account.py | 6 ------ .../test_by_type/test_configured_link_addon.py | 14 ++++---------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/addon_service/tests/test_by_type/test_authorized_link_account.py b/addon_service/tests/test_by_type/test_authorized_link_account.py index 2749c8be..f518bbb6 100644 --- a/addon_service/tests/test_by_type/test_authorized_link_account.py +++ b/addon_service/tests/test_by_type/test_authorized_link_account.py @@ -4,7 +4,6 @@ patch, ) -from django.conf import settings from django.test import TestCase from django.urls import reverse from rest_framework.test import APITestCase @@ -102,7 +101,6 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf = MockOSF() self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) @@ -289,7 +287,6 @@ def setUp(self): def test_get(self): request = get_test_request(user=self._user) request.session = {"user_reference_uri": self._user.user_uri} - request.COOKIES = {settings.USER_REFERENCE_COOKIE: self._user.user_uri} _resp = self._view( request, pk=self._account.pk, @@ -317,7 +314,6 @@ def test_get(self): def test_owner_access(self): request = get_test_request(user=self._user) request.session = {"user_reference_uri": self._user.user_uri} - request.COOKIES = {settings.USER_REFERENCE_COOKIE: self._user.user_uri} _resp = self._view( request, pk=self._account.pk, @@ -329,7 +325,6 @@ def test_wrong_user(self): self._mock_osf.configure_assumed_caller(_another_user.user_uri) request = get_test_request(user=_another_user) request.session = {"user_reference_uri": _another_user.user_uri} - request.COOKIES = {settings.USER_REFERENCE_COOKIE: _another_user.user_uri} _resp = self._view( request, pk=self._account.pk, @@ -344,7 +339,6 @@ def setUpTestData(cls): cls._external_service = _factories.ExternalLinkOAuth2ServiceFactory() def setUp(self): - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf = MockOSF() self._mock_osf.configure_assumed_caller(self._user.user_uri) diff --git a/addon_service/tests/test_by_type/test_configured_link_addon.py b/addon_service/tests/test_by_type/test_configured_link_addon.py index 1f484bad..f2d46a20 100644 --- a/addon_service/tests/test_by_type/test_configured_link_addon.py +++ b/addon_service/tests/test_by_type/test_configured_link_addon.py @@ -4,7 +4,6 @@ patch, ) -from django.conf import settings from django.core.exceptions import ValidationError from django.test import TestCase from django.urls import reverse @@ -50,7 +49,6 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf = MockOSF() self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" @@ -117,13 +115,13 @@ def test_resource_type_property(self): self._addon.save() refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) - self.assertEqual(refreshed.resource_type, "BOOK") + self.assertEqual(refreshed.resource_type, "Book") self._addon.resource_type = SupportedResourceTypes.Other self._addon.save() refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) - self.assertEqual(refreshed.resource_type, "DATASET") + self.assertEqual(refreshed.resource_type, "Other") def test_validator_valid_types(self): try: @@ -195,7 +193,6 @@ def setUp(self): def test_get(self): request = get_test_request(user=self._user) request.session = {"user_reference_uri": self._user.user_uri} - request.COOKIES = {settings.USER_REFERENCE_COOKIE: self._user.user_uri} _resp = self._view( request, @@ -218,7 +215,6 @@ def test_get(self): def test_owner_access(self): request = get_test_request(user=self._user) request.session = {"user_reference_uri": self._user.user_uri} - request.COOKIES = {settings.USER_REFERENCE_COOKIE: self._user.user_uri} _resp = self._view( request, @@ -232,7 +228,6 @@ def test_wrong_user(self): request = get_test_request(user=_another_user) request.session = {"user_reference_uri": _another_user.user_uri} - request.COOKIES = {settings.USER_REFERENCE_COOKIE: _another_user.user_uri} _resp = self._view( request, @@ -281,7 +276,6 @@ def setUp(self): self.addCleanup(self.instance_blocking_patcher.stop) def test_create_addon(self): - self.client.cookies[settings.USER_REFERENCE_COOKIE] = self._user.user_uri self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" ) @@ -321,8 +315,8 @@ def test_create_addon(self): self.assertEqual(_resp.status_code, HTTPStatus.CREATED) - self.assertEqual(_resp.data["resource_type"], "DATASET") + self.assertEqual(_resp.data["resource_type"], "Other") addon = db.ConfiguredLinkAddon.objects.get(id=_resp.data["id"]) self.assertEqual(addon.target_id, "some-target-id") - self.assertEqual(addon.resource_type, "DATASET") + self.assertEqual(addon.resource_type, "Other") From 909979859da81c543b05427aaa9a6515bb3463ec Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Fri, 2 May 2025 18:12:57 +0300 Subject: [PATCH 073/100] Fix tests --- .../test_configured_link_addon.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/addon_service/tests/test_by_type/test_configured_link_addon.py b/addon_service/tests/test_by_type/test_configured_link_addon.py index f2d46a20..b6f3fbeb 100644 --- a/addon_service/tests/test_by_type/test_configured_link_addon.py +++ b/addon_service/tests/test_by_type/test_configured_link_addon.py @@ -75,7 +75,7 @@ def test_get(self): _resp = self.client.get(self._detail_path) self.assertEqual(_resp.status_code, HTTPStatus.OK) self.assertEqual(_resp.data["target_id"], self._addon.target_id) - self.assertEqual(_resp.data["resource_type"], self._addon.resource_type) + self.assertEqual(_resp.data["resource_type"], self._addon.resource_type.name) def test_methods_not_allowed(self): _methods_not_allowed = { @@ -115,13 +115,15 @@ def test_resource_type_property(self): self._addon.save() refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) - self.assertEqual(refreshed.resource_type, "Book") + self.assertEqual(refreshed.resource_type, SupportedResourceTypes.Book) + self.assertEqual(refreshed.resource_type.name, "Book") self._addon.resource_type = SupportedResourceTypes.Other self._addon.save() refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) - self.assertEqual(refreshed.resource_type, "Other") + self.assertEqual(refreshed.resource_type, SupportedResourceTypes.Other) + self.assertEqual(refreshed.resource_type.name, "Other") def test_validator_valid_types(self): try: @@ -202,7 +204,9 @@ def test_get(self): with self.subTest("Confirm expected attributes"): self.assertEqual(_resp.data["target_id"], self._addon.target_id) - self.assertEqual(_resp.data["resource_type"], self._addon.resource_type) + self.assertEqual( + _resp.data["resource_type"], self._addon.resource_type.name + ) self.assertIn("connected_operation_names", _resp.data) with self.subTest("Confirm expected relationships"): @@ -286,7 +290,7 @@ def test_create_addon(self): "type": "configured-link-addons", "attributes": { "target_id": "some-target-id", - "resource_type": "DATASET", + "resource_type": "Other", "connected_capabilities": ["ACCESS"], "authorized_resource_uri": self._resource.resource_uri, }, @@ -315,8 +319,9 @@ def test_create_addon(self): self.assertEqual(_resp.status_code, HTTPStatus.CREATED) - self.assertEqual(_resp.data["resource_type"], "Other") + self.assertEqual(_resp.data["resource_type"], SupportedResourceTypes.Other.name) addon = db.ConfiguredLinkAddon.objects.get(id=_resp.data["id"]) self.assertEqual(addon.target_id, "some-target-id") - self.assertEqual(addon.resource_type, "Other") + self.assertEqual(addon.resource_type, SupportedResourceTypes.Other) + self.assertEqual(addon.resource_type.name, "Other") From bfb75abf1f8f31a7e0adc6aba9c09f57b8c36484 Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Mon, 19 May 2025 16:31:31 +0300 Subject: [PATCH 074/100] Fix existing tests and add new for configured link addon null value --- .../test_configured_link_addon_null_values.py | 174 +++++++++ .../test_null_resource_link_addon.py | 360 ++++++++++++++++++ 2 files changed, 534 insertions(+) create mode 100644 addon_service/tests/test_by_type/test_configured_link_addon_null_values.py create mode 100644 addon_service/tests/test_by_type/test_null_resource_link_addon.py diff --git a/addon_service/tests/test_by_type/test_configured_link_addon_null_values.py b/addon_service/tests/test_by_type/test_configured_link_addon_null_values.py new file mode 100644 index 00000000..3af55fab --- /dev/null +++ b/addon_service/tests/test_by_type/test_configured_link_addon_null_values.py @@ -0,0 +1,174 @@ +import json +from http import HTTPStatus +from unittest.mock import patch + +from django.test import TestCase +from django.urls import reverse +from rest_framework.test import APITestCase + +from addon_service import models as db +from addon_service.configured_addon.link.serializers import ( + ConfiguredLinkAddonSerializer, +) +from addon_service.tests import _factories +from addon_service.tests._helpers import MockOSF +from addon_toolkit import AddonCapabilities +from addon_toolkit.interfaces.link import SupportedResourceTypes + + +def mock_target_url(self): + return f"https://example.com/dataset/{self.target_id}" if self.target_id else None + + +class TestConfiguredLinkAddonNullValues(TestCase): + @classmethod + def setUpTestData(cls): + cls._addon = _factories.ConfiguredLinkAddonFactory() + + def setUp(self): + self._mock_osf = MockOSF() + self.enterContext(self._mock_osf.mocking()) + + self.target_url_patcher = patch( + "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", + mock_target_url, + ) + self.target_url_patcher.start() + self.addCleanup(self.target_url_patcher.stop) + + def test_resource_type_setter_with_none(self): + addon = _factories.ConfiguredLinkAddonFactory() + + addon.resource_type = SupportedResourceTypes.Other + addon.save() + refreshed = db.ConfiguredLinkAddon.objects.get(id=addon.id) + self.assertEqual(refreshed.resource_type, SupportedResourceTypes.Other) + + addon.resource_type = None + addon.save() + + refreshed = db.ConfiguredLinkAddon.objects.get(id=addon.id) + self.assertEqual( + refreshed.int_resource_type, SupportedResourceTypes.Other.value + ) + + +class TestConfiguredLinkAddonSerializerNullValues(APITestCase): + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._resource = _factories.ResourceReferenceFactory() + cls._authorized_account = _factories.AuthorizedLinkAccountFactory( + account_owner=cls._user + ) + cls._addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=cls._resource, + base_account=cls._authorized_account, + ) + + def setUp(self): + super().setUp() + self.client.cookies["osf"] = self._user.user_uri + self._mock_osf = MockOSF() + self._mock_osf.configure_user_role( + self._user.user_uri, self._resource.resource_uri, "admin" + ) + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + self.target_url_patcher = patch( + "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", + mock_target_url, + ) + self.target_url_patcher.start() + self.addCleanup(self.target_url_patcher.stop) + + @property + def _detail_path(self): + return reverse("configured-link-addons-detail", kwargs={"pk": self._addon.pk}) + + @property + def _list_path(self): + return reverse("configured-link-addons-list") + + def test_serializer_with_null_target_id(self): + serializer = ConfiguredLinkAddonSerializer( + data={ + "target_id": None, + "connected_capabilities": [AddonCapabilities.ACCESS.name], + "base_account": { + "id": self._authorized_account.id, + "type": "authorized-link-accounts", + }, + "authorized_resource": self._resource.resource_uri, + }, + context={"request": None}, + ) + + self.assertTrue( + serializer.is_valid(), f"Serializer errors: {serializer.errors}" + ) + + def test_serializer_with_null_resource_type(self): + serializer = ConfiguredLinkAddonSerializer( + data={ + "resource_type": None, + "connected_capabilities": [AddonCapabilities.ACCESS.name], + "base_account": { + "id": self._authorized_account.id, + "type": "authorized-link-accounts", + }, + "authorized_resource": self._resource.resource_uri, + }, + context={"request": None}, + ) + + self.assertTrue( + serializer.is_valid(), f"Serializer errors: {serializer.errors}" + ) + + def test_api_update_with_null_values(self): + self._addon.target_id = "test-dataset-id" + self._addon.resource_type = SupportedResourceTypes.Other + self._addon.save() + + request_data = { + "data": { + "id": str(self._addon.id), + "type": "configured-link-addons", + "attributes": {"target_id": None, "resource_type": None}, + } + } + + response = self.client.patch( + self._detail_path, + data=json.dumps(request_data), + content_type="application/vnd.api+json", + ) + + self.assertEqual( + response.status_code, HTTPStatus.OK, f"Response content: {response.content}" + ) + + self._addon.refresh_from_db() + self.assertIsNone(self._addon.target_id) + self.assertEqual( + self._addon.int_resource_type, SupportedResourceTypes.Other.value + ) + + def test_create_with_null_values(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + target_id=None, + int_resource_type=None, + ) + + addon.int_resource_type = None + addon.save(full_clean=False) + + addon.refresh_from_db() + self.assertIsNone(addon.target_id) + + self.assertIsNone(addon.int_resource_type) + self.assertIsNone(addon.resource_type) diff --git a/addon_service/tests/test_by_type/test_null_resource_link_addon.py b/addon_service/tests/test_by_type/test_null_resource_link_addon.py new file mode 100644 index 00000000..ebc2e598 --- /dev/null +++ b/addon_service/tests/test_by_type/test_null_resource_link_addon.py @@ -0,0 +1,360 @@ +import json +from http import HTTPStatus +from unittest.mock import patch + +from django.core.exceptions import ValidationError +from django.test import TestCase +from django.urls import reverse +from rest_framework.test import APITestCase + +from addon_service.configured_addon.link.models import is_supported_resource_type +from addon_service.configured_addon.link.serializers import VerifiedLinkSerializer +from addon_service.tests import _factories +from addon_service.tests._helpers import MockOSF +from addon_toolkit.interfaces.link import SupportedResourceTypes + + +def mock_target_url(self): + return f"https://example.com/dataset/{self.target_id}" if self.target_id else None + + +class TestNullResourceTypeValidator(TestCase): + + def test_validator_with_none(self): + try: + is_supported_resource_type(None) + except ValidationError: + self.fail("Validator raised ValidationError unexpectedly when given None") + + def test_validator_with_valid_types(self): + try: + is_supported_resource_type(SupportedResourceTypes.Dataset.value) + is_supported_resource_type(SupportedResourceTypes.Journal.value) + is_supported_resource_type(SupportedResourceTypes.Software.value) + except ValidationError: + self.fail("Validator raised ValidationError unexpectedly on valid types") + + def test_validator_with_invalid_types(self): + with self.assertRaises(ValidationError): + is_supported_resource_type(-999) + + combined = ( + SupportedResourceTypes.Dataset.value | SupportedResourceTypes.Journal.value + ) + with self.assertRaises(ValidationError): + is_supported_resource_type(combined) + + +class TestConfiguredLinkAddonWithNullFields(TestCase): + + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._resource = _factories.ResourceReferenceFactory() + cls._authorized_account = _factories.AuthorizedLinkAccountFactory( + account_owner=cls._user + ) + + def setUp(self): + self._mock_osf = MockOSF() + self.enterContext(self._mock_osf.mocking()) + + self.task_patcher = patch( + "addon_service.configured_addon.link.models.app.send_task" + ) + self.mock_task = self.task_patcher.start() + self.addCleanup(self.task_patcher.stop) + + self.target_url_patcher = patch( + "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", + mock_target_url, + ) + self.target_url_patcher.start() + self.addCleanup(self.target_url_patcher.stop) + + self.mock_task.reset_mock() + + def test_create_with_null_fields(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + target_id=None, + int_resource_type=None, + ) + + self.mock_task.reset_mock() + + addon.refresh_from_db() + self.assertIsNone(addon.target_id) + + self.assertIsNotNone(addon.int_resource_type) + self.assertIsNotNone(addon.resource_type) + + self.mock_task.reset_mock() + addon.save() + doi_task_called = False + for call in self.mock_task.call_args_list: + if ( + call[0][0] + == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" + ): + doi_task_called = True + break + + self.assertFalse( + doi_task_called, + "DOI metadata task should not be called when fields are missing", + ) + + def test_resource_type_property_with_none(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + ) + self.assertIsNotNone(addon.resource_type) + + def test_resource_type_setter_with_value(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + ) + + addon.resource_type = SupportedResourceTypes.Dataset + addon.save() + + addon.refresh_from_db() + self.assertEqual(addon.int_resource_type, SupportedResourceTypes.Dataset.value) + self.assertEqual(addon.resource_type, SupportedResourceTypes.Dataset) + + def test_save_with_missing_fields(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + target_id=None, + ) + + self.mock_task.reset_mock() + addon.save() + + doi_task_called = False + for call in self.mock_task.call_args_list: + if ( + call[0][0] + == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" + ): + doi_task_called = True + break + + self.assertFalse( + doi_task_called, + "DOI metadata task should not be called when target_id is missing", + ) + + def test_save_with_target_id_missing(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + target_id=None, + ) + addon.resource_type = SupportedResourceTypes.Dataset + + self.mock_task.reset_mock() + addon.save() + + doi_task_called = False + for call in self.mock_task.call_args_list: + if ( + call[0][0] + == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" + ): + doi_task_called = True + break + + self.assertFalse( + doi_task_called, + "DOI metadata task should not be called when target_id is missing", + ) + + def test_save_with_resource_type_missing(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + target_id="test-id", + ) + addon.int_resource_type = 0 + + self.mock_task.reset_mock() + addon.save() + + doi_called = False + for call in self.mock_task.call_args_list: + if ( + call[0][0] + == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" + and call[1]["kwargs"]["target_guid"] == self._resource.guid + ): + doi_called = True + + self.assertFalse( + doi_called, "DOI task should not be called when resource_type is missing" + ) + + def test_save_with_all_fields_present(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + target_id="test-id", + ) + addon.resource_type = SupportedResourceTypes.Dataset + + self.mock_task.reset_mock() + addon.save() + + doi_called = False + for call in self.mock_task.call_args_list: + if ( + call[0][0] + == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" + and call[1]["kwargs"]["target_guid"] == self._resource.guid + ): + doi_called = True + + self.assertTrue( + doi_called, "DOI task should be called when all fields are present" + ) + + def test_target_url_with_null_target_id(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + target_id=None, + ) + + self.assertIsNone(addon.target_url()) + + +class TestConfiguredLinkAddonSerializerNullFields(APITestCase): + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._resource = _factories.ResourceReferenceFactory() + cls._authorized_account = _factories.AuthorizedLinkAccountFactory( + account_owner=cls._user + ) + cls._addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=cls._resource, + base_account=cls._authorized_account, + target_id="test-id", + ) + + def setUp(self): + super().setUp() + self.client.cookies["osf"] = self._user.user_uri + self._mock_osf = MockOSF() + self._mock_osf.configure_user_role( + self._user.user_uri, self._resource.resource_uri, "admin" + ) + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + self.target_url_patcher = patch( + "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", + mock_target_url, + ) + self.target_url_patcher.start() + self.addCleanup(self.target_url_patcher.stop) + + @property + def _detail_path(self): + return reverse("configured-link-addons-detail", kwargs={"pk": self._addon.pk}) + + @property + def _list_path(self): + return reverse("configured-link-addons-list") + + def test_api_update_with_null_fields(self): + self._addon.target_id = "test-id" + self._addon.resource_type = SupportedResourceTypes.Dataset + self._addon.save() + request_data = { + "data": { + "id": str(self._addon.id), + "type": "configured-link-addons", + "attributes": {"target_id": None}, + } + } + + response = self.client.patch( + self._detail_path, + data=json.dumps(request_data), + content_type="application/vnd.api+json", + ) + + self.assertEqual( + response.status_code, HTTPStatus.OK, f"Response content: {response.content}" + ) + + self._addon.refresh_from_db() + self.assertIsNone(self._addon.target_id) + self.assertIsNotNone(self._addon.int_resource_type) + + +class TestVerifiedLinkSerializer(TestCase): + + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._resource = _factories.ResourceReferenceFactory() + cls._authorized_account = _factories.AuthorizedLinkAccountFactory( + account_owner=cls._user + ) + cls._addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=cls._resource, + base_account=cls._authorized_account, + target_id="test-id", + ) + + def setUp(self): + self.target_url_patcher = patch( + "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", + mock_target_url, + ) + self.target_url_patcher.start() + self.addCleanup(self.target_url_patcher.stop) + + def test_serialize_with_all_values(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + target_id="test-id", + ) + addon.resource_type = SupportedResourceTypes.Dataset + + serializer = VerifiedLinkSerializer(addon) + data = serializer.data + + self.assertEqual(data["target_id"], "test-id") + self.assertEqual(data["resource_type"], "Dataset") + self.assertEqual(data["target_url"], "https://example.com/dataset/test-id") + self.assertEqual( + data["service_name"], + addon.base_account.external_service.external_service_name, + ) + + def test_serialize_with_null_values(self): + addon = _factories.ConfiguredLinkAddonFactory( + authorized_resource=self._resource, + base_account=self._authorized_account, + target_id=None, + ) + + serializer = VerifiedLinkSerializer(addon) + data = serializer.data + + self.assertIsNone(data["target_id"]) + self.assertIsNotNone(data["resource_type"]) + self.assertIsNone(data["target_url"]) + self.assertEqual( + data["service_name"], + addon.base_account.external_service.external_service_name, + ) From 02c5900d97c0a78041ad9fdd7068dc5209457b7e Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Mon, 19 May 2025 17:56:59 +0300 Subject: [PATCH 075/100] Fix failed tests --- .../test_authorized_link_account.py | 5 + .../test_configured_link_addon.py | 126 +++++++++--------- 2 files changed, 70 insertions(+), 61 deletions(-) diff --git a/addon_service/tests/test_by_type/test_authorized_link_account.py b/addon_service/tests/test_by_type/test_authorized_link_account.py index f518bbb6..5530a1bb 100644 --- a/addon_service/tests/test_by_type/test_authorized_link_account.py +++ b/addon_service/tests/test_by_type/test_authorized_link_account.py @@ -101,6 +101,7 @@ def setUpTestData(cls): def setUp(self): super().setUp() + self.client.cookies["osf"] = self._user.user_uri self._mock_osf = MockOSF() self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) @@ -287,6 +288,7 @@ def setUp(self): def test_get(self): request = get_test_request(user=self._user) request.session = {"user_reference_uri": self._user.user_uri} + request.COOKIES = {"osf": self._user.user_uri} _resp = self._view( request, pk=self._account.pk, @@ -314,6 +316,7 @@ def test_get(self): def test_owner_access(self): request = get_test_request(user=self._user) request.session = {"user_reference_uri": self._user.user_uri} + request.COOKIES = {"osf": self._user.user_uri} _resp = self._view( request, pk=self._account.pk, @@ -325,6 +328,7 @@ def test_wrong_user(self): self._mock_osf.configure_assumed_caller(_another_user.user_uri) request = get_test_request(user=_another_user) request.session = {"user_reference_uri": _another_user.user_uri} + request.COOKIES = {"osf": _another_user.user_uri} _resp = self._view( request, pk=self._account.pk, @@ -339,6 +343,7 @@ def setUpTestData(cls): cls._external_service = _factories.ExternalLinkOAuth2ServiceFactory() def setUp(self): + self.client.cookies["osf"] = self._user.user_uri self._mock_osf = MockOSF() self._mock_osf.configure_assumed_caller(self._user.user_uri) diff --git a/addon_service/tests/test_by_type/test_configured_link_addon.py b/addon_service/tests/test_by_type/test_configured_link_addon.py index b6f3fbeb..48e5ab31 100644 --- a/addon_service/tests/test_by_type/test_configured_link_addon.py +++ b/addon_service/tests/test_by_type/test_configured_link_addon.py @@ -49,6 +49,7 @@ def setUpTestData(cls): def setUp(self): super().setUp() + self.client.cookies["osf"] = self._user.user_uri self._mock_osf = MockOSF() self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" @@ -108,7 +109,9 @@ def setUp(self): def test_can_load(self): _addon_from_db = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) self.assertEqual(self._addon.target_id, _addon_from_db.target_id) - self.assertEqual(self._addon.resource_type, _addon_from_db.resource_type) + self.assertEqual( + self._addon.resource_type.value, _addon_from_db.resource_type.value + ) def test_resource_type_property(self): self._addon.resource_type = SupportedResourceTypes.Book @@ -116,18 +119,16 @@ def test_resource_type_property(self): refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) self.assertEqual(refreshed.resource_type, SupportedResourceTypes.Book) - self.assertEqual(refreshed.resource_type.name, "Book") - self._addon.resource_type = SupportedResourceTypes.Other + self._addon.resource_type = SupportedResourceTypes.Dataset self._addon.save() refreshed = db.ConfiguredLinkAddon.objects.get(id=self._addon.id) - self.assertEqual(refreshed.resource_type, SupportedResourceTypes.Other) - self.assertEqual(refreshed.resource_type.name, "Other") + self.assertEqual(refreshed.resource_type, SupportedResourceTypes.Dataset) def test_validator_valid_types(self): try: - is_supported_resource_type(SupportedResourceTypes.Other.value) + is_supported_resource_type(SupportedResourceTypes.Dataset.value) is_supported_resource_type(SupportedResourceTypes.Journal.value) is_supported_resource_type(SupportedResourceTypes.Software.value) except ValidationError: @@ -138,14 +139,14 @@ def test_validator_invalid_type(self): is_supported_resource_type(-999) combined = ( - SupportedResourceTypes.Other.value | SupportedResourceTypes.Journal.value + SupportedResourceTypes.Dataset.value | SupportedResourceTypes.Journal.value ) with self.assertRaises(ValidationError): is_supported_resource_type(combined) def test_validation_on_save(self): self._addon.int_resource_type = ( - SupportedResourceTypes.Other.value | SupportedResourceTypes.Journal.value + SupportedResourceTypes.Dataset.value | SupportedResourceTypes.Journal.value ) with self.assertRaises(ValidationError): self._addon.clean_fields() @@ -195,6 +196,7 @@ def setUp(self): def test_get(self): request = get_test_request(user=self._user) request.session = {"user_reference_uri": self._user.user_uri} + request.COOKIES = {"osf": self._user.user_uri} _resp = self._view( request, @@ -219,6 +221,7 @@ def test_get(self): def test_owner_access(self): request = get_test_request(user=self._user) request.session = {"user_reference_uri": self._user.user_uri} + request.COOKIES = {"osf": self._user.user_uri} _resp = self._view( request, @@ -232,6 +235,7 @@ def test_wrong_user(self): request = get_test_request(user=_another_user) request.session = {"user_reference_uri": _another_user.user_uri} + request.COOKIES = {"osf": _another_user.user_uri} _resp = self._view( request, @@ -246,11 +250,12 @@ def setUpTestData(cls): cls._user = _factories.UserReferenceFactory() cls._resource = _factories.ResourceReferenceFactory() cls._authorized_account = _factories.AuthorizedLinkAccountFactory( - account_owner=cls._user, - authorized_capabilities=AddonCapabilities.ACCESS, + account_owner=cls._user ) def setUp(self): + super().setUp() + self.client.cookies["osf"] = self._user.user_uri self._mock_osf = MockOSF() self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" @@ -262,66 +267,65 @@ def setUp(self): "addon_service.configured_addon.link.models.ConfiguredLinkAddon.target_url", mock_target_url, ) - self.instance_patcher = patch( - "addon_service.addon_imp.instantiation.get_link_addon_instance", - mock_get_link_addon_instance, - ) - self.instance_blocking_patcher = patch( + self.target_url_patcher.start() + self.addCleanup(self.target_url_patcher.stop) + + self.instantiate_patcher = patch( "addon_service.addon_imp.instantiation.get_link_addon_instance__blocking", mock_get_link_addon_instance, ) + self.instantiate_patcher.start() + self.addCleanup(self.instantiate_patcher.stop) - self.target_url_patcher.start() - self.instance_patcher.start() - self.instance_blocking_patcher.start() - - self.addCleanup(self.target_url_patcher.stop) - self.addCleanup(self.instance_patcher.stop) - self.addCleanup(self.instance_blocking_patcher.stop) + @property + def _list_path(self): + return reverse("configured-link-addons-list") def test_create_addon(self): - self._mock_osf.configure_user_role( - self._user.user_uri, self._resource.resource_uri, "admin" - ) - self._mock_osf.configure_assumed_caller(self._user.user_uri) + _initial_count = db.ConfiguredLinkAddon.objects.count() - request_data = { - "data": { - "type": "configured-link-addons", - "attributes": { - "target_id": "some-target-id", - "resource_type": "Other", - "connected_capabilities": ["ACCESS"], - "authorized_resource_uri": self._resource.resource_uri, - }, - "relationships": { - "base_account": { - "data": { - "type": "authorized-link-accounts", - "id": str(self._authorized_account.id), - } - }, - "authorized_resource": { - "data": { - "type": "resource-references", - "id": str(self._resource.id), - } - }, - }, - } - } + _target_id = "12345" + _resource_type = SupportedResourceTypes.Dataset + + import json _resp = self.client.post( - reverse("configured-link-addons-list"), - request_data, - format="vnd.api+json", + self._list_path, + data=json.dumps( + { + "data": { + "type": "configured-link-addons", + "attributes": { + "target_id": _target_id, + "resource_type": _resource_type.name, + "connected_capabilities": [AddonCapabilities.ACCESS.name], + "authorized_resource_uri": self._resource.resource_uri, + }, + "relationships": { + "base_account": { + "data": { + "id": str(self._authorized_account.id), + "type": "authorized-link-accounts", + } + }, + "authorized_resource": { + "data": { + "id": self._resource.resource_uri, + "type": "resource-references", + } + }, + }, + } + } + ), + content_type="application/vnd.api+json", ) - self.assertEqual(_resp.status_code, HTTPStatus.CREATED) - - self.assertEqual(_resp.data["resource_type"], SupportedResourceTypes.Other.name) + self.assertEqual( + _resp.status_code, HTTPStatus.CREATED, f"Response content: {_resp.content}" + ) - addon = db.ConfiguredLinkAddon.objects.get(id=_resp.data["id"]) - self.assertEqual(addon.target_id, "some-target-id") - self.assertEqual(addon.resource_type, SupportedResourceTypes.Other) - self.assertEqual(addon.resource_type.name, "Other") + self.assertEqual(db.ConfiguredLinkAddon.objects.count(), _initial_count + 1) + _created = db.ConfiguredLinkAddon.objects.get(id=_resp.data["id"]) + self.assertEqual(_created.target_id, _target_id) + self.assertEqual(_created.resource_type.value, _resource_type.value) From 502a7ddb5d4582aa5f804d368fdbdadaf96d8b3c Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Tue, 27 May 2025 16:47:06 +0300 Subject: [PATCH 076/100] Add new tests --- .../test_enhanced_resource_types.py | 220 +++++++++++++++++ .../test_by_type/test_session_sharing.py | 221 ++++++++++++++++++ 2 files changed, 441 insertions(+) create mode 100644 addon_service/tests/test_by_type/test_enhanced_resource_types.py create mode 100644 addon_service/tests/test_by_type/test_session_sharing.py diff --git a/addon_service/tests/test_by_type/test_enhanced_resource_types.py b/addon_service/tests/test_by_type/test_enhanced_resource_types.py new file mode 100644 index 00000000..dac2bb99 --- /dev/null +++ b/addon_service/tests/test_by_type/test_enhanced_resource_types.py @@ -0,0 +1,220 @@ +from http import HTTPStatus + +from django.test import TestCase +from rest_framework.test import APITestCase + +from addon_service.common.credentials_formats import CredentialsFormats +from addon_service.common.enum_serializers import EnumNameMultipleChoiceField +from addon_service.tests import _factories +from addon_service.tests._helpers import MockOSF +from addon_toolkit.interfaces.link import SupportedResourceTypes + + +class TestResourceTypesSorting(APITestCase): + + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._external_service = _factories.ExternalLinkServiceFactory( + supported_resource_types=SupportedResourceTypes.Dataset + | SupportedResourceTypes.Book + | SupportedResourceTypes.Software, + credentials_format=CredentialsFormats.PERSONAL_ACCESS_TOKEN, + ) + + def setUp(self): + super().setUp() + self.client.cookies["osf"] = self._user.user_uri + self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + def test_external_service_resource_types_sorted_alphabetically(self): + response = self.client.get( + f"/v1/external-link-services/{self._external_service.id}/" + ) + + self.assertEqual(response.status_code, HTTPStatus.OK) + + resource_types = response.data.get("supported_resource_types", []) + + expected_order = ["Book", "Dataset", "Software"] + self.assertEqual(resource_types, expected_order) + + def test_enum_field_returns_sorted_choices(self): + expected_names = sorted([rt.name for rt in SupportedResourceTypes]) + + actual_names = expected_names + + self.assertEqual(actual_names, expected_names) + + +class TestResourceTypeValidation(TestCase): + + def test_valid_resource_types(self): + valid_types = [ + SupportedResourceTypes.Book, + SupportedResourceTypes.Dataset, + SupportedResourceTypes.Software, + SupportedResourceTypes.Journal, + SupportedResourceTypes.OutputManagementPlan, + SupportedResourceTypes.Other, + ] + + for resource_type in valid_types: + with self.subTest(resource_type=resource_type): + addon = _factories.ConfiguredLinkAddonFactory() + addon.resource_type = resource_type + + addon.full_clean() + self.assertEqual(addon.resource_type, resource_type) + + def test_resource_type_enum_values(self): + self.assertEqual(SupportedResourceTypes.Dataset.name, "Dataset") + self.assertEqual(SupportedResourceTypes.Book.name, "Book") + self.assertEqual(SupportedResourceTypes.Software.name, "Software") + self.assertEqual(SupportedResourceTypes.Journal.name, "Journal") + self.assertEqual( + SupportedResourceTypes.OutputManagementPlan.name, "OutputManagementPlan" + ) + self.assertEqual(SupportedResourceTypes.Other.name, "Other") + + def test_resource_type_integer_conversion(self): + addon = _factories.ConfiguredLinkAddonFactory() + + addon.resource_type = SupportedResourceTypes.Dataset + addon.save() + + addon.refresh_from_db() + self.assertEqual(addon.int_resource_type, SupportedResourceTypes.Dataset.value) + self.assertEqual(addon.resource_type, SupportedResourceTypes.Dataset) + + +class TestResourceTypeSerializerField(TestCase): + + def test_enum_field_serialization(self): + field = EnumNameMultipleChoiceField(enum_cls=SupportedResourceTypes) + + result = field.to_representation(SupportedResourceTypes.Dataset) + self.assertEqual(result, ["Dataset"]) + + combined_value = SupportedResourceTypes.Dataset | SupportedResourceTypes.Book + result = field.to_representation(combined_value) + self.assertEqual(set(result), {"Dataset", "Book"}) + + def test_enum_field_deserialization(self): + field = EnumNameMultipleChoiceField(enum_cls=SupportedResourceTypes) + + result = field.to_internal_value(["Dataset"]) + self.assertEqual(result, SupportedResourceTypes.Dataset) + + result = field.to_internal_value(["Dataset", "Book"]) + expected = SupportedResourceTypes.Dataset | SupportedResourceTypes.Book + self.assertEqual(result, expected) + + def test_enum_field_sorted_output(self): + field = EnumNameMultipleChoiceField(enum_cls=SupportedResourceTypes) + + combined_value = ( + SupportedResourceTypes.Software + | SupportedResourceTypes.Book + | SupportedResourceTypes.Dataset + ) + + result = field.to_representation(combined_value) + + expected_order = ["Book", "Dataset", "Software"] + self.assertEqual(result, expected_order) + + +class TestResourceTypeNaming(TestCase): + + def test_output_management_plan_naming(self): + resource_type = SupportedResourceTypes.OutputManagementPlan + + self.assertEqual(resource_type.name, "OutputManagementPlan") + field = EnumNameMultipleChoiceField(enum_cls=SupportedResourceTypes) + result = field.to_representation(resource_type) + self.assertEqual(result, ["OutputManagementPlan"]) + + def test_all_resource_type_names_valid(self): + for resource_type in SupportedResourceTypes: + with self.subTest(resource_type=resource_type): + self.assertTrue(resource_type.name.isidentifier()) + + self.assertNotIn(" ", resource_type.name) + self.assertNotIn("-", resource_type.name) + + def test_resource_type_display_names(self): + expected_names = { + SupportedResourceTypes.Book: "Book", + SupportedResourceTypes.Dataset: "Dataset", + SupportedResourceTypes.Software: "Software", + SupportedResourceTypes.Journal: "Journal", + SupportedResourceTypes.OutputManagementPlan: "OutputManagementPlan", + SupportedResourceTypes.Other: "Other", + } + + for resource_type, expected_name in expected_names.items(): + with self.subTest(resource_type=resource_type): + self.assertEqual(resource_type.name, expected_name) + + +class TestResourceTypeAPIConsistency(APITestCase): + + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + + def setUp(self): + super().setUp() + self.client.cookies["osf"] = self._user.user_uri + self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self.enterContext(self._mock_osf.mocking()) + + +class TestResourceTypeCompatibility(TestCase): + + def test_resource_type_values_stable(self): + known_values = { + SupportedResourceTypes.Book: 4, + SupportedResourceTypes.Dataset: 512, + SupportedResourceTypes.Journal: 32768, + SupportedResourceTypes.Other: 2147483648, + SupportedResourceTypes.Software: 33554432, + SupportedResourceTypes.OutputManagementPlan: 262144, + } + + for resource_type, expected_value in known_values.items(): + with self.subTest(resource_type=resource_type): + self.assertEqual(resource_type.value, expected_value) + + def test_resource_type_combinations(self): + combined = SupportedResourceTypes.Dataset | SupportedResourceTypes.Book + + self.assertIn(SupportedResourceTypes.Dataset, combined) + self.assertIn(SupportedResourceTypes.Book, combined) + + self.assertNotIn(SupportedResourceTypes.Software, combined) + + field = EnumNameMultipleChoiceField(enum_cls=SupportedResourceTypes) + result = field.to_representation(combined) + self.assertEqual(set(result), {"Dataset", "Book"}) + + def test_legacy_resource_type_handling(self): + addon = _factories.ConfiguredLinkAddonFactory( + credentials_format=CredentialsFormats.PERSONAL_ACCESS_TOKEN, + ) + + addon.int_resource_type = None + addon.save() + + addon.refresh_from_db() + self.assertIsNone(addon.resource_type) + + addon.full_clean() + addon.resource_type = SupportedResourceTypes.Other + addon.save() + addon.refresh_from_db() + self.assertEqual(addon.resource_type, SupportedResourceTypes.Other) diff --git a/addon_service/tests/test_by_type/test_session_sharing.py b/addon_service/tests/test_by_type/test_session_sharing.py new file mode 100644 index 00000000..af94d0e4 --- /dev/null +++ b/addon_service/tests/test_by_type/test_session_sharing.py @@ -0,0 +1,221 @@ +from http import HTTPStatus +from unittest.mock import ( + Mock, + patch, +) + +import itsdangerous +from django.conf import settings +from django.contrib.sessions.backends.cache import SessionStore +from django.test import ( + RequestFactory, + TestCase, +) +from rest_framework.test import APITestCase + +from addon_service.tests import _factories +from addon_service.tests._helpers import MockOSF +from app.middleware import UnsignCookieSessionMiddleware + + +class TestSessionSharingMiddleware(TestCase): + + def setUp(self): + self.factory = RequestFactory() + self.middleware = UnsignCookieSessionMiddleware(Mock()) + + def test_process_request_with_valid_cookie(self): + session_key = "test-session-key-123" + signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) + signed_cookie = signer.sign(session_key) + + request = self.factory.get("/") + request.COOKIES = {settings.SESSION_COOKIE_NAME: signed_cookie} + + result = self.middleware.process_request(request) + + self.assertIsNone(result) + self.assertIsInstance(request.session, SessionStore) + self.assertEqual(request.session.session_key, session_key) + + def test_process_request_with_invalid_signature(self): + invalid_cookie = "invalid.cookie.signature" + + request = self.factory.get("/") + request.COOKIES = {settings.SESSION_COOKIE_NAME: invalid_cookie} + + result = self.middleware.process_request(request) + + self.assertIsNone(result) + + def test_process_request_without_cookie(self): + request = self.factory.get("/") + request.COOKIES = {} + result = self.middleware.process_request(request) + + self.assertIsNone(result) + self.assertIsInstance(request.session, SessionStore) + self.assertIsNone(request.session.session_key) + + def test_process_request_with_empty_cookie(self): + request = self.factory.get("/") + request.COOKIES = {settings.SESSION_COOKIE_NAME: ""} + + result = self.middleware.process_request(request) + + self.assertIsNone(result) + self.assertIsInstance(request.session, SessionStore) + + @patch("app.middleware.SessionStore") + def test_session_store_instantiation(self, mock_session_store): + session_key = "test-key-456" + signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) + signed_cookie = signer.sign(session_key) + + request = self.factory.get("/") + request.COOKIES = {settings.SESSION_COOKIE_NAME: signed_cookie} + + self.middleware.process_request(request) + + mock_session_store.assert_called_with(session_key=session_key) + + +class TestRedisSessionBackend(TestCase): + + def test_session_engine_configuration(self): + self.assertEqual( + settings.SESSION_ENGINE, "django.contrib.sessions.backends.cache" + ) + + def test_redis_cache_configuration(self): + cache_config = settings.CACHES["default"] + self.assertEqual( + cache_config["BACKEND"], "django.core.cache.backends.redis.RedisCache" + ) + self.assertEqual(cache_config["LOCATION"], settings.REDIS_HOST) + + def test_session_cookie_name_matches_osf(self): + self.assertEqual(settings.SESSION_COOKIE_NAME, settings.OSF_AUTH_COOKIE_NAME) + + +class TestSessionSharingIntegration(APITestCase): + + @classmethod + def setUpTestData(cls): + cls._user = _factories.UserReferenceFactory() + cls._resource = _factories.ResourceReferenceFactory() + + def setUp(self): + super().setUp() + self._mock_osf = MockOSF() + self._mock_osf.configure_assumed_caller(self._user.user_uri) + self._mock_osf.configure_user_role( + self._user.user_uri, self._resource.resource_uri, "admin" + ) + self.enterContext(self._mock_osf.mocking()) + + def test_api_access_with_shared_session(self): + session_key = "shared-session-123" + signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) + signed_cookie = signer.sign(session_key) + + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = signed_cookie + + session_store = SessionStore(session_key=session_key) + session_store["user_reference_uri"] = self._user.user_uri + session_store.save() + + url = f"/v1/resource-references/{self._resource.pk}/" + response = self.client.get(url) + + self.assertEqual(response.status_code, HTTPStatus.OK) + + def test_session_persistence_across_requests(self): + session_key = "persistent-session-456" + signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) + signed_cookie = signer.sign(session_key) + + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = signed_cookie + + session_store = SessionStore(session_key=session_key) + session_store["user_reference_uri"] = self._user.user_uri + session_store["test_data"] = "persistent_value" + session_store.save() + + url = f"/v1/resource-references/{self._resource.pk}/" + response1 = self.client.get(url) + self.assertEqual(response1.status_code, HTTPStatus.OK) + + response2 = self.client.get(url) + self.assertEqual(response2.status_code, HTTPStatus.OK) + + response3 = self.client.get(url) + self.assertEqual(response3.status_code, HTTPStatus.OK) + + self.assertEqual(response1.data["id"], response2.data["id"]) + self.assertEqual(response2.data["id"], response3.data["id"]) + + def test_invalid_session_handling(self): + invalid_session_key = "invalid-session-789" + signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) + signed_cookie = signer.sign(invalid_session_key) + + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = signed_cookie + + url = f"/v1/resource-references/{self._resource.pk}/" + response = self.client.get(url) + self.assertIn( + response.status_code, + [ + HTTPStatus.OK, # MockOSF allows access + HTTPStatus.UNAUTHORIZED, + HTTPStatus.FORBIDDEN, + ], + ) + + +class TestCookieSigningCompatibility(TestCase): + + def test_cookie_signing_with_osf_secret(self): + test_value = "test-session-value" + signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) + signed_value = signer.sign(test_value) + self.assertIsInstance(signed_value, (str, bytes)) + + unsigned_value = signer.unsign(signed_value) + self.assertEqual( + ( + unsigned_value.decode() + if isinstance(unsigned_value, bytes) + else unsigned_value + ), + test_value, + ) + + def test_bad_signature_handling(self): + signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) + + with self.assertRaises(itsdangerous.BadSignature): + signer.unsign("tampered.signature.value") + + +class TestSessionSecurityFeatures(TestCase): + + def test_cors_credentials_enabled(self): + self.assertTrue(settings.CORS_ALLOW_CREDENTIALS) + + @patch("app.middleware.ensure_str") + def test_session_key_encoding(self, mock_ensure_str): + mock_ensure_str.return_value = "encoded-session-key" + + session_key = b"binary-session-key" + signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) + signed_cookie = signer.sign(session_key) + + request = RequestFactory().get("/") + request.COOKIES = {settings.SESSION_COOKIE_NAME: signed_cookie} + + middleware = UnsignCookieSessionMiddleware(Mock()) + middleware.process_request(request) + + mock_ensure_str.assert_called_once() From e382c5623dc77b1320f4a21045d90e68f6b1dc12 Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Fri, 13 Jun 2025 15:29:42 +0300 Subject: [PATCH 077/100] Add integration tests for VRL --- .../test_verified_links_workflow.py | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 addon_service/tests/test_by_type/test_verified_links_workflow.py diff --git a/addon_service/tests/test_by_type/test_verified_links_workflow.py b/addon_service/tests/test_by_type/test_verified_links_workflow.py new file mode 100644 index 00000000..4389bcbd --- /dev/null +++ b/addon_service/tests/test_by_type/test_verified_links_workflow.py @@ -0,0 +1,279 @@ +from unittest.mock import ( + MagicMock, + patch, +) + +from django.test import TestCase +from rest_framework.test import APITestCase + +from addon_service.external_service.link.models import LinkSupportedFeatures +from addon_service.tests import _factories +from addon_toolkit.interfaces.link import SupportedResourceTypes + + +class TestVerifiedLinksWorkflow(APITestCase): + + def setUp(self): + self._service = _factories.ExternalLinkOAuth2ServiceFactory( + display_name="Dataverse Link Service", + supported_features=LinkSupportedFeatures.ADD_UPDATE_FILES, + ) + self._account = _factories.AuthorizedLinkAccountFactory( + external_service=self._service + ) + + @patch("app.celery.app.send_task") + def test_create_link_addon_workflow(self, mock_send_task): + addon = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service, + base_account=self._account, + target_id="dataset_123", + resource_type=SupportedResourceTypes.Dataset, + ) + + addon.resource_uri = "https://osf.io/abc123/" + addon.save() + + self.assertIsNotNone(addon.pk) + self.assertEqual(addon.target_id, "dataset_123") + self.assertEqual(addon.resource_uri, "https://osf.io/abc123/") + + mock_send_task.assert_called() + + +class TestCeleryIntegration(TestCase): + + def setUp(self): + self._service = _factories.ExternalLinkOAuth2ServiceFactory( + display_name="Test Service", + supported_features=LinkSupportedFeatures.ADD_UPDATE_FILES, + ) + self._account = _factories.AuthorizedLinkAccountFactory( + external_service=self._service + ) + + @patch("app.celery.app.send_task") + def test_celery_task_on_complete_addon(self, mock_send_task): + addon = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service, + base_account=self._account, + target_id="dataset_123", + resource_type=SupportedResourceTypes.Dataset, + ) + + addon.resource_uri = "https://osf.io/abc123/" + addon.save() + + self.assertTrue(mock_send_task.called) + call_args_list = mock_send_task.call_args_list + + log_calls = [ + call for call in call_args_list if "osf.tasks.log_gv_addon" in call[0] + ] + self.assertTrue(len(log_calls) > 0) + + @patch("app.celery.app.send_task") + def test_celery_task_not_triggered_incomplete(self, mock_send_task): + addon = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service, + base_account=self._account, + target_id="", + resource_type=SupportedResourceTypes.Dataset, + ) + + addon.resource_uri = "https://osf.io/abc123/" + addon.save() + + self.assertTrue(mock_send_task.called) + call_args_list = mock_send_task.call_args_list + + log_calls = [ + call for call in call_args_list if "osf.tasks.log_gv_addon" in call[0] + ] + verification_calls = [ + call + for call in call_args_list + if "website.identifiers.tasks.task__update_verified_links" in call[0] + ] + + self.assertTrue(len(log_calls) > 0) + self.assertEqual(len(verification_calls), 0) + + +class TestMultipleAddonsPerResource(TestCase): + + def setUp(self): + self._service1 = _factories.ExternalLinkOAuth2ServiceFactory( + display_name="Dataverse", + supported_features=LinkSupportedFeatures.ADD_UPDATE_FILES, + ) + self._service2 = _factories.ExternalLinkOAuth2ServiceFactory( + display_name="Zenodo", + supported_features=LinkSupportedFeatures.ADD_UPDATE_FILES, + ) + + self._account1 = _factories.AuthorizedLinkAccountFactory( + external_service=self._service1 + ) + self._account2 = _factories.AuthorizedLinkAccountFactory( + external_service=self._service2 + ) + + @patch("app.celery.app.send_task") + def test_multiple_addons_same_resource(self, mock_send_task): + resource_uri = "https://osf.io/abc123/" + + addon1 = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service1, + base_account=self._account1, + target_id="dataverse_dataset_123", + resource_type=SupportedResourceTypes.Dataset, + ) + addon1.resource_uri = resource_uri + addon1.save() + + addon2 = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service2, + base_account=self._account2, + target_id="zenodo_record_456", + resource_type=SupportedResourceTypes.Dataset, + ) + addon2.resource_uri = resource_uri + addon2.save() + + self.assertIsNotNone(addon1.pk) + self.assertIsNotNone(addon2.pk) + self.assertEqual(addon1.resource_uri, addon2.resource_uri) + self.assertNotEqual(addon1.target_id, addon2.target_id) + + +class TestDataverseLinkIntegration(TestCase): + """Test Dataverse-specific link functionality.""" + + def setUp(self): + self._service = _factories.ExternalLinkOAuth2ServiceFactory( + display_name="Harvard Dataverse", + browser_base_url="https://dataverse.harvard.edu/", + supported_features=LinkSupportedFeatures.ADD_UPDATE_FILES, + ) + self._account = _factories.AuthorizedLinkAccountFactory( + external_service=self._service + ) + + @patch("addon_service.addon_imp.instantiation.get_link_addon_instance__blocking") + @patch("app.celery.app.send_task") + def test_dataverse_url_generation(self, mock_send_task, mock_get_instance): + url = "https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/12345" + + mock_addon = MagicMock() + mock_addon.build_url_for_id = url + mock_get_instance.return_value = mock_addon + + addon = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service, + base_account=self._account, + target_id="dataset/doi:10.7910/DVN/12345", + resource_type=SupportedResourceTypes.Dataset, + ) + addon.resource_uri = "https://osf.io/abc123/" + addon.save() + + target_url = addon.target_url() + self.assertIsNotNone(target_url) + self.assertIn("dataverse.harvard.edu", target_url) + + @patch("app.celery.app.send_task") + def test_different_resource_types(self, mock_send_task): + """Test creating addons with different resource types.""" + addon1 = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service, + base_account=self._account, + target_id="dataset_123", + resource_type=SupportedResourceTypes.Dataset, + ) + addon1.resource_uri = "https://osf.io/abc123/" + addon1.save() + + addon2 = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service, + base_account=self._account, + target_id="article_456", + resource_type=SupportedResourceTypes.JournalArticle, + ) + addon2.resource_uri = "https://osf.io/def456/" + addon2.save() + + self.assertEqual(addon1.resource_type, SupportedResourceTypes.Dataset) + self.assertEqual(addon2.resource_type, SupportedResourceTypes.JournalArticle) + + @patch("app.celery.app.send_task") + def test_target_url_with_empty_target_id(self, mock_send_task): + """Test that target_url returns None when target_id is empty.""" + addon = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service, + base_account=self._account, + target_id="", + resource_type=SupportedResourceTypes.Dataset, + ) + addon.resource_uri = "https://osf.io/abc123/" + addon.save() + + self.assertIsNone(addon.target_url()) + + +class TestResourceValidation(TestCase): + + def setUp(self): + self._service = _factories.ExternalLinkOAuth2ServiceFactory( + display_name="Test Service", + supported_features=LinkSupportedFeatures.ADD_UPDATE_FILES, + ) + self._account = _factories.AuthorizedLinkAccountFactory( + external_service=self._service + ) + + @patch("app.celery.app.send_task") + def test_addon_creation_with_valid_resource_uri(self, mock_send_task): + valid_uris = [ + "https://osf.io/abc123/", + "https://staging.osf.io/def456/", + "http://localhost:5000/xyz789/", + ] + + for uri in valid_uris: + with self.subTest(uri=uri): + addon = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service, + base_account=self._account, + target_id="test_123", + resource_type=SupportedResourceTypes.Dataset, + ) + addon.resource_uri = uri + addon.save() + self.assertEqual(addon.resource_uri, uri) + + @patch("app.celery.app.send_task") + def test_addon_creation_different_resource_uris(self, mock_send_task): + uris = [ + "https://osf.io/project1/", + "https://osf.io/project2/", + "https://staging.osf.io/project3/", + ] + + addons = [] + for i, uri in enumerate(uris): + addon = _factories.ConfiguredLinkAddonFactory( + external_link_service=self._service, + base_account=self._account, + target_id=f"target_{i}", + resource_type=SupportedResourceTypes.Dataset, + ) + addon.resource_uri = uri + addon.save() + addons.append(addon) + + for addon in addons: + self.assertIsNotNone(addon.pk) + + uri_set = {addon.resource_uri for addon in addons} + self.assertEqual(len(uri_set), len(addons)) From a57a9527251fe7f95330c6113b866e6f44fbfc2f Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Fri, 13 Jun 2025 15:44:17 +0300 Subject: [PATCH 078/100] Enhance ExternalLinkServiceFactory to support optional features --- addon_service/tests/_factories.py | 7 +++++++ .../tests/test_by_type/test_verified_links_workflow.py | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/addon_service/tests/_factories.py b/addon_service/tests/_factories.py index f57f8455..3aeea18f 100644 --- a/addon_service/tests/_factories.py +++ b/addon_service/tests/_factories.py @@ -210,9 +210,11 @@ def _create( credentials_format=CredentialsFormats.PERSONAL_ACCESS_TOKEN, service_type=ServiceTypes.PUBLIC, supported_resource_types=None, + supported_features=None, *args, **kwargs, ): + from addon_service.external_service.link.models import LinkSupportedFeatures from addon_toolkit.interfaces.link import SupportedResourceTypes api_base_url = "" @@ -223,11 +225,16 @@ def _create( supported_resource_types or SupportedResourceTypes.Other ) + supported_features = ( + supported_features or LinkSupportedFeatures.ADD_UPDATE_FILES + ) + return super()._create( model_class=model_class, int_credentials_format=credentials_format.value, int_service_type=service_type.value, int_supported_resource_types=supported_resource_types.value, + int_supported_features=supported_features.value, api_base_url=api_base_url, *args, **kwargs, diff --git a/addon_service/tests/test_by_type/test_verified_links_workflow.py b/addon_service/tests/test_by_type/test_verified_links_workflow.py index 4389bcbd..bd5c9b2d 100644 --- a/addon_service/tests/test_by_type/test_verified_links_workflow.py +++ b/addon_service/tests/test_by_type/test_verified_links_workflow.py @@ -163,10 +163,11 @@ def setUp(self): @patch("addon_service.addon_imp.instantiation.get_link_addon_instance__blocking") @patch("app.celery.app.send_task") def test_dataverse_url_generation(self, mock_send_task, mock_get_instance): - url = "https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/12345" + async def mock_build_url(target_id): + return "https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/12345" mock_addon = MagicMock() - mock_addon.build_url_for_id = url + mock_addon.build_url_for_id = mock_build_url mock_get_instance.return_value = mock_addon addon = _factories.ConfiguredLinkAddonFactory( From 0cd7cc17b0539f72fc5fd59bba58acc56468dbca Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Fri, 13 Jun 2025 15:52:32 +0300 Subject: [PATCH 079/100] fix test_save_with_all_fields_present --- .../test_null_resource_link_addon.py | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/addon_service/tests/test_by_type/test_null_resource_link_addon.py b/addon_service/tests/test_by_type/test_null_resource_link_addon.py index ebc2e598..b29e4ba5 100644 --- a/addon_service/tests/test_by_type/test_null_resource_link_addon.py +++ b/addon_service/tests/test_by_type/test_null_resource_link_addon.py @@ -94,10 +94,7 @@ def test_create_with_null_fields(self): addon.save() doi_task_called = False for call in self.mock_task.call_args_list: - if ( - call[0][0] - == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" - ): + if call[0][0] == "website.identifiers.tasks.task__update_verified_links": doi_task_called = True break @@ -138,10 +135,7 @@ def test_save_with_missing_fields(self): doi_task_called = False for call in self.mock_task.call_args_list: - if ( - call[0][0] - == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" - ): + if call[0][0] == "website.identifiers.tasks.task__update_verified_links": doi_task_called = True break @@ -163,10 +157,7 @@ def test_save_with_target_id_missing(self): doi_task_called = False for call in self.mock_task.call_args_list: - if ( - call[0][0] - == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" - ): + if call[0][0] == "website.identifiers.tasks.task__update_verified_links": doi_task_called = True break @@ -189,8 +180,7 @@ def test_save_with_resource_type_missing(self): doi_called = False for call in self.mock_task.call_args_list: if ( - call[0][0] - == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" + call[0][0] == "website.identifiers.tasks.task__update_verified_links" and call[1]["kwargs"]["target_guid"] == self._resource.guid ): doi_called = True @@ -213,8 +203,7 @@ def test_save_with_all_fields_present(self): doi_called = False for call in self.mock_task.call_args_list: if ( - call[0][0] - == "website.identifiers.tasks.task__update_doi_metadata_with_verified_links" + call[0][0] == "website.identifiers.tasks.task__update_verified_links" and call[1]["kwargs"]["target_guid"] == self._resource.guid ): doi_called = True From d020b10532b6bdffa4e00936650254d273022ab5 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 27 May 2025 13:18:20 +0300 Subject: [PATCH 080/100] base swagger imp --- app/settings.py | 49 +++++++++++++- app/urls.py | 16 +++++ poetry.lock | 168 ++++++++++++++++++++++++++++++++++++------------ pyproject.toml | 2 + 4 files changed, 193 insertions(+), 42 deletions(-) diff --git a/app/settings.py b/app/settings.py index 814f2c82..5efd6297 100644 --- a/app/settings.py +++ b/app/settings.py @@ -107,6 +107,7 @@ "rest_framework_json_api", "addon_service", "django_celery_beat", + "drf_spectacular", ] MIDDLEWARE = [ @@ -171,7 +172,37 @@ }, }, } - +SWAGGER_SETTINGS = { + "DEFAULT_AUTO_SCHEMA_CLASS": "drf_yasg_json_api.inspectors.SwaggerAutoSchema", # Overridden + "DEFAULT_FIELD_INSPECTORS": [ + "drf_yasg_json_api.inspectors.NamesFormatFilter", # Replaces CamelCaseJSONFilter + "drf_yasg.inspectors.RecursiveFieldInspector", + "drf_yasg_json_api.inspectors.XPropertiesFilter", # Added + "drf_yasg_json_api.inspectors.JSONAPISerializerSmartInspector", # Added + "drf_yasg.inspectors.ReferencingSerializerInspector", + "drf_yasg_json_api.inspectors.IntegerIDFieldInspector", # Added + "drf_yasg.inspectors.ChoiceFieldInspector", + "drf_yasg.inspectors.FileFieldInspector", + "drf_yasg.inspectors.DictFieldInspector", + "drf_yasg.inspectors.JSONFieldInspector", + "drf_yasg.inspectors.HiddenFieldInspector", + "drf_yasg_json_api.inspectors.ManyRelatedFieldInspector", # Added + "drf_yasg_json_api.inspectors.IntegerPrimaryKeyRelatedFieldInspector", # Added + "drf_yasg.inspectors.RelatedFieldInspector", + "drf_yasg.inspectors.SerializerMethodFieldInspector", + "drf_yasg.inspectors.SimpleFieldInspector", + "drf_yasg.inspectors.StringDefaultFieldInspector", + ], + "DEFAULT_FILTER_INSPECTORS": [ + "drf_yasg_json_api.inspectors.DjangoFilterInspector", # Added (optional), requires django_filter + "drf_yasg.inspectors.CoreAPICompatInspector", + ], + "DEFAULT_PAGINATOR_INSPECTORS": [ + "drf_yasg_json_api.inspectors.DjangoRestResponsePagination", # Added + "drf_yasg.inspectors.DjangoRestResponsePagination", + "drf_yasg.inspectors.CoreAPICompatInspector", + ], +} if env.OSFDB_HOST: DATABASES["osf"] = { "ENGINE": "django.db.backends.postgresql", @@ -193,12 +224,13 @@ REST_FRAMEWORK = { "PAGE_SIZE": 101, "EXCEPTION_HANDLER": "addon_service.exception_handler.api_exception_handler", - "DEFAULT_PAGINATION_CLASS": "rest_framework_json_api.pagination.JsonApiPageNumberPagination", + "DEFAULT_PAGINATION_CLASS": "drf_spectacular_jsonapi.schemas.pagination.JsonApiPageNumberPagination", "DEFAULT_PARSER_CLASSES": ( "rest_framework_json_api.parsers.JSONParser", "rest_framework.parsers.FormParser", "rest_framework.parsers.MultiPartParser", ), + "DEFAULT_SCHEMA_CLASS": "drf_spectacular_jsonapi.schemas.openapi.JsonApiAutoSchema", "DEFAULT_RENDERER_CLASSES": ( "rest_framework_json_api.renderers.JSONRenderer", "rest_framework_json_api.renderers.BrowsableAPIRenderer", @@ -219,6 +251,19 @@ "TEST_REQUEST_DEFAULT_FORMAT": "vnd.api+json", } +SPECTACULAR_SETTINGS = { + "TITLE": "GravyValet API", + "DESCRIPTION": "Addons service designed for use with OSF", + "VERSION": "1.0.0", + "SERVE_INCLUDE_SCHEMA": False, + "COMPONENT_SPLIT_REQUEST": True, + "PREPROCESSING_HOOKS": ["drf_spectacular_jsonapi.hooks.fix_nested_path_parameters"], + "EXTENSIONS_INFO": { + "drf_spectacular_jsonapi.extensions.JSONAPIResourceExtension": {}, + "drf_spectacular_jsonapi.extensions.JSONAPIErrorExtension": {}, + }, + # OTHER SETTINGS +} # Password validation # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators diff --git a/app/urls.py b/app/urls.py index 1e307e51..74285096 100644 --- a/app/urls.py +++ b/app/urls.py @@ -5,6 +5,11 @@ path, ) from django.views.generic.base import RedirectView +from drf_spectacular.views import ( + SpectacularAPIView, + SpectacularRedocView, + SpectacularSwaggerView, +) urlpatterns = [ @@ -15,6 +20,17 @@ RedirectView.as_view(url="/static/gravyvalet_code_docs/index.html"), name="docs-root", ), + path("api/schema/", SpectacularAPIView.as_view(), name="schema"), + path( + "api/schema/swagger-ui/", + SpectacularSwaggerView.as_view(url_name="schema"), + name="swagger-ui", + ), + path( + "api/schema/redoc/", + SpectacularRedocView.as_view(url_name="schema"), + name="redoc", + ), ] if "silk" in settings.INSTALLED_APPS: diff --git a/poetry.lock b/poetry.lock index e4a9d38d..379dc305 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -113,7 +113,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] [[package]] name = "aiosignal" @@ -173,12 +173,12 @@ files = [ ] [package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "autobahn" @@ -199,13 +199,13 @@ setuptools = "*" txaio = ">=21.2.1" [package.extras] -all = ["PyGObject (>=3.40.0)", "argon2-cffi (>=20.1.0)", "attrs (>=20.3.0)", "base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "cffi (>=1.14.5)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "flatbuffers (>=22.12.6)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "msgpack (>=1.0.2) ; platform_python_implementation == \"CPython\"", "passlib (>=1.7.4)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "py-ubjson (>=0.16.1)", "pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "python-snappy (>=0.6.0)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "rlp (>=2.0.1)", "service-identity (>=18.1.0)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "twisted (>=24.3.0)", "u-msgpack-python (>=2.1) ; platform_python_implementation != \"CPython\"", "ujson (>=4.0.2) ; platform_python_implementation == \"CPython\"", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)", "zope.interface (>=5.2.0)"] +all = ["PyGObject (>=3.40.0)", "argon2-cffi (>=20.1.0)", "attrs (>=20.3.0)", "base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "cffi (>=1.14.5)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "flatbuffers (>=22.12.6)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "msgpack (>=1.0.2)", "passlib (>=1.7.4)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "py-ubjson (>=0.16.1)", "pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "python-snappy (>=0.6.0)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "rlp (>=2.0.1)", "service-identity (>=18.1.0)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "twisted (>=24.3.0)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)", "zope.interface (>=5.2.0)"] compress = ["python-snappy (>=0.6.0)"] -dev = ["backports.tempfile (>=1.0)", "build (>=1.2.1)", "bumpversion (>=0.5.3)", "codecov (>=2.0.15)", "flake8 (<5)", "humanize (>=0.5.1)", "mypy (>=0.610) ; python_version >= \"3.4\" and platform_python_implementation != \"PyPy\"", "passlib", "pep8-naming (>=0.3.3)", "pip (>=9.0.1)", "pyenchant (>=1.6.6)", "pyflakes (>=1.0.0)", "pyinstaller (>=4.2)", "pylint (>=1.9.2)", "pytest (>=3.4.2)", "pytest-aiohttp", "pytest-asyncio (>=0.14.0)", "pytest-runner (>=2.11.1)", "pyyaml (>=4.2b4)", "qualname", "sphinx (>=1.7.1)", "sphinx-autoapi (>=1.7.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-images (>=0.9.1)", "tox (>=4.2.8)", "tox-gh-actions (>=2.2.0)", "twine (>=3.3.0)", "twisted (>=22.10.0)", "txaio (>=20.4.1)", "watchdog (>=0.8.3)", "wheel (>=0.36.2)", "yapf (==0.29.0)"] +dev = ["backports.tempfile (>=1.0)", "build (>=1.2.1)", "bumpversion (>=0.5.3)", "codecov (>=2.0.15)", "flake8 (<5)", "humanize (>=0.5.1)", "mypy (>=0.610)", "passlib", "pep8-naming (>=0.3.3)", "pip (>=9.0.1)", "pyenchant (>=1.6.6)", "pyflakes (>=1.0.0)", "pyinstaller (>=4.2)", "pylint (>=1.9.2)", "pytest (>=3.4.2)", "pytest-aiohttp", "pytest-asyncio (>=0.14.0)", "pytest-runner (>=2.11.1)", "pyyaml (>=4.2b4)", "qualname", "sphinx (>=1.7.1)", "sphinx-autoapi (>=1.7.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-images (>=0.9.1)", "tox (>=4.2.8)", "tox-gh-actions (>=2.2.0)", "twine (>=3.3.0)", "twisted (>=22.10.0)", "txaio (>=20.4.1)", "watchdog (>=0.8.3)", "wheel (>=0.36.2)", "yapf (==0.29.0)"] encryption = ["pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "service-identity (>=18.1.0)"] nvx = ["cffi (>=1.14.5)"] scram = ["argon2-cffi (>=20.1.0)", "cffi (>=1.14.5)", "passlib (>=1.7.4)"] -serialization = ["cbor2 (>=5.2.0)", "flatbuffers (>=22.12.6)", "msgpack (>=1.0.2) ; platform_python_implementation == \"CPython\"", "py-ubjson (>=0.16.1)", "u-msgpack-python (>=2.1) ; platform_python_implementation != \"CPython\"", "ujson (>=4.0.2) ; platform_python_implementation == \"CPython\""] +serialization = ["cbor2 (>=5.2.0)", "flatbuffers (>=22.12.6)", "msgpack (>=1.0.2)", "py-ubjson (>=0.16.1)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)"] twisted = ["attrs (>=20.3.0)", "twisted (>=24.3.0)", "zope.interface (>=5.2.0)"] ui = ["PyGObject (>=3.40.0)"] xbr = ["base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "rlp (>=2.0.1)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)"] @@ -330,33 +330,33 @@ vine = ">=5.1.0,<6.0" arangodb = ["pyArango (>=2.0.2)"] auth = ["cryptography (==44.0.2)"] azureblockblob = ["azure-identity (>=1.19.0)", "azure-storage-blob (>=12.15.0)"] -brotli = ["brotli (>=1.0.0) ; platform_python_implementation == \"CPython\"", "brotlipy (>=0.7.0) ; platform_python_implementation == \"PyPy\""] +brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] cassandra = ["cassandra-driver (>=3.25.0,<4)"] consul = ["python-consul2 (==0.1.5)"] cosmosdbsql = ["pydocumentdb (==2.3.5)"] -couchbase = ["couchbase (>=3.0.0) ; platform_python_implementation != \"PyPy\" and (platform_system != \"Windows\" or python_version < \"3.10\")"] +couchbase = ["couchbase (>=3.0.0)"] couchdb = ["pycouchdb (==1.16.0)"] django = ["Django (>=2.2.28)"] dynamodb = ["boto3 (>=1.26.143)"] elasticsearch = ["elastic-transport (<=8.17.1)", "elasticsearch (<=8.17.2)"] -eventlet = ["eventlet (>=0.32.0) ; python_version < \"3.10\""] +eventlet = ["eventlet (>=0.32.0)"] gcs = ["google-cloud-firestore (==2.20.1)", "google-cloud-storage (>=2.10.0)", "grpcio (==1.67.0)"] gevent = ["gevent (>=1.5.0)"] -librabbitmq = ["librabbitmq (>=2.0.0) ; python_version < \"3.11\""] -memcache = ["pylibmc (==1.6.3) ; platform_system != \"Windows\""] +librabbitmq = ["librabbitmq (>=2.0.0)"] +memcache = ["pylibmc (==1.6.3)"] mongodb = ["pymongo (==4.10.1)"] msgpack = ["msgpack (==1.1.0)"] pydantic = ["pydantic (>=2.4)"] pymemcache = ["python-memcached (>=1.61)"] -pyro = ["pyro4 (==4.82) ; python_version < \"3.11\""] +pyro = ["pyro4 (==4.82)"] pytest = ["pytest-celery[all] (>=1.2.0,<1.3.0)"] redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] s3 = ["boto3 (>=1.26.143)"] slmq = ["softlayer_messaging (>=1.0.3)"] -solar = ["ephem (==4.2) ; platform_python_implementation != \"PyPy\""] +solar = ["ephem (==4.2)"] sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.4)", "urllib3 (>=1.26.16)"] -tblib = ["tblib (>=1.3.0) ; python_version < \"3.8.0\"", "tblib (>=1.5.0) ; python_version >= \"3.8.0\""] +tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] yaml = ["PyYAML (>=3.10)"] zookeeper = ["kazoo (>=1.3.1)"] zstd = ["zstandard (==0.23.0)"] @@ -718,10 +718,10 @@ files = [ cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version >= \"3.8\""] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""] -pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.1)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] @@ -901,6 +901,70 @@ django-filter = ["django-filter (>=2.4)"] django-polymorphic = ["django-polymorphic (>=3.0)"] openapi = ["pyyaml (>=5.4)", "uritemplate (>=3.0.1)"] +[[package]] +name = "drf-extensions" +version = "0.8.0" +description = "Extensions for Django REST Framework" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "drf_extensions-0.8.0-py2.py3-none-any.whl", hash = "sha256:ab7bd854c9061c27ab55233b66d758001e5c2d81aaa9d117cbbe1c9ea49c77ab"}, + {file = "drf_extensions-0.8.0.tar.gz", hash = "sha256:c3f27bca74a2def53e8454a5c7b327595195df51e121743120b2f51ef5a52aaa"}, +] + +[package.dependencies] +Django = ">=2.2,<6.0" +djangorestframework = ">=3.10.3" +packaging = ">=24.1" + +[[package]] +name = "drf-spectacular" +version = "0.28.0" +description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "drf_spectacular-0.28.0-py3-none-any.whl", hash = "sha256:856e7edf1056e49a4245e87a61e8da4baff46c83dbc25be1da2df77f354c7cb4"}, + {file = "drf_spectacular-0.28.0.tar.gz", hash = "sha256:2c778a47a40ab2f5078a7c42e82baba07397bb35b074ae4680721b2805943061"}, +] + +[package.dependencies] +Django = ">=2.2" +djangorestframework = ">=3.10.3" +inflection = ">=0.3.1" +jsonschema = ">=2.6.0" +PyYAML = ">=5.1" +uritemplate = ">=2.0.0" + +[package.extras] +offline = ["drf-spectacular-sidecar"] +sidecar = ["drf-spectacular-sidecar"] + +[[package]] +name = "drf-spectacular-jsonapi" +version = "0.5.2" +description = "open api 3 schema generator for drf-json-api package based on drf-spectacular package." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [] +develop = false + +[package.dependencies] +Django = ">=3.2" +djangorestframework = ">=3.13" +djangorestframework-jsonapi = ">=6.0.0" +drf-extensions = ">=0.7.1" +drf-spectacular = ">=0.25.0" + +[package.source] +type = "git" +url = "ssh://git@github.com/opaduchak/drf-spectacular-json-api.git" +reference = "osf-fixes" +resolved_reference = "f280b852dbee1667c21a1998e7009567bfc113c4" + [[package]] name = "factory-boy" version = "3.3.1" @@ -950,7 +1014,7 @@ files = [ [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8) ; python_version < \"3.11\""] +typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" @@ -1257,7 +1321,7 @@ azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6. confluentkafka = ["confluent-kafka (>=2.2.0)"] consul = ["python-consul2 (==0.1.5)"] gcpubsub = ["google-cloud-monitoring (>=2.16.0)", "google-cloud-pubsub (>=2.18.4)", "grpcio (==1.67.0)", "protobuf (==4.25.5)"] -librabbitmq = ["librabbitmq (>=2.0.0) ; python_version < \"3.11\""] +librabbitmq = ["librabbitmq (>=2.0.0)"] mongodb = ["pymongo (>=4.1.1)"] msgpack = ["msgpack (==1.1.0)"] pyro = ["pyro4 (==4.82)"] @@ -1505,6 +1569,18 @@ files = [ {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + [[package]] name = "pdoc" version = "14.5.1" @@ -1703,8 +1779,8 @@ typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.2.6) ; implementation_name != \"pypy\""] -c = ["psycopg-c (==3.2.6) ; implementation_name != \"pypy\""] +binary = ["psycopg-binary (==3.2.6)"] +c = ["psycopg-c (==3.2.6)"] dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] @@ -1951,7 +2027,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2080,7 +2156,7 @@ requests = ">=2.30.0,<3.0" urllib3 = ">=1.25.10,<3.0" [package.extras] -tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli ; python_version < \"3.11\"", "tomli-w", "types-PyYAML", "types-requests"] +tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli", "tomli-w", "types-PyYAML", "types-requests"] [[package]] name = "rpds-py" @@ -2302,9 +2378,9 @@ files = [ ] [package.extras] -core = ["importlib-metadata (>=6) ; python_version < \"3.10\"", "importlib-resources (>=5.10.2) ; python_version < \"3.9\"", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-ruff (<0.4) ; platform_system == \"Windows\"", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "pytest-ruff (>=0.3.2) ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -2381,19 +2457,19 @@ typing-extensions = ">=4.2.0" zope-interface = ">=5" [package.extras] -all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"] dev = ["coverage (>=7.5,<8.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "python-subunit (>=1.4,<2.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)"] dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"] -gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"] -macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] -mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"] -osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] -serial = ["pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\""] +macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"] +osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +serial = ["pyserial (>=3.0)", "pywin32 (!=226)"] test = ["cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"] tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"] -windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"] +windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"] [[package]] name = "txaio" @@ -2423,7 +2499,7 @@ files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -markers = {dev = "python_version == \"3.12\"", release = "python_version == \"3.12\""} +markers = {dev = "python_version < \"3.13\"", release = "python_version < \"3.13\""} [[package]] name = "tzdata" @@ -2438,6 +2514,18 @@ files = [ ] markers = {dev = "sys_platform == \"win32\"", release = "sys_platform == \"win32\""} +[[package]] +name = "uritemplate" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, + {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, +] + [[package]] name = "urllib3" version = "2.2.2" @@ -2451,7 +2539,7 @@ files = [ ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -2487,7 +2575,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "wcwidth" @@ -2653,4 +2741,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "bc1ceda74871d00d3d7b3777b6ba9edfb18c1a3af3497451a2800815818e272a" +content-hash = "b53f0026f04c386056607fc06558e28dcc265180e2cc7bf63cba00a78138c234" diff --git a/pyproject.toml b/pyproject.toml index 0923906a..5fb51e3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,8 @@ django-celery-beat = "^2.7.0" tqdm = "^4.67.1" itsdangerous = "^2.2.0" redis = "^5.2.1" +drf-spectacular = "^0.28.0" +drf-spectacular-jsonapi = {git = "git@github.com:opaduchak/drf-spectacular-json-api.git", branch = "osf-fixes"} [tool.poetry.group.dev.dependencies] From 5fdc139c7e9041051052c98048a145735346fbfd Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 27 May 2025 15:57:52 +0300 Subject: [PATCH 081/100] removed yasg settings --- app/settings.py | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/app/settings.py b/app/settings.py index 5efd6297..fada488a 100644 --- a/app/settings.py +++ b/app/settings.py @@ -172,37 +172,7 @@ }, }, } -SWAGGER_SETTINGS = { - "DEFAULT_AUTO_SCHEMA_CLASS": "drf_yasg_json_api.inspectors.SwaggerAutoSchema", # Overridden - "DEFAULT_FIELD_INSPECTORS": [ - "drf_yasg_json_api.inspectors.NamesFormatFilter", # Replaces CamelCaseJSONFilter - "drf_yasg.inspectors.RecursiveFieldInspector", - "drf_yasg_json_api.inspectors.XPropertiesFilter", # Added - "drf_yasg_json_api.inspectors.JSONAPISerializerSmartInspector", # Added - "drf_yasg.inspectors.ReferencingSerializerInspector", - "drf_yasg_json_api.inspectors.IntegerIDFieldInspector", # Added - "drf_yasg.inspectors.ChoiceFieldInspector", - "drf_yasg.inspectors.FileFieldInspector", - "drf_yasg.inspectors.DictFieldInspector", - "drf_yasg.inspectors.JSONFieldInspector", - "drf_yasg.inspectors.HiddenFieldInspector", - "drf_yasg_json_api.inspectors.ManyRelatedFieldInspector", # Added - "drf_yasg_json_api.inspectors.IntegerPrimaryKeyRelatedFieldInspector", # Added - "drf_yasg.inspectors.RelatedFieldInspector", - "drf_yasg.inspectors.SerializerMethodFieldInspector", - "drf_yasg.inspectors.SimpleFieldInspector", - "drf_yasg.inspectors.StringDefaultFieldInspector", - ], - "DEFAULT_FILTER_INSPECTORS": [ - "drf_yasg_json_api.inspectors.DjangoFilterInspector", # Added (optional), requires django_filter - "drf_yasg.inspectors.CoreAPICompatInspector", - ], - "DEFAULT_PAGINATOR_INSPECTORS": [ - "drf_yasg_json_api.inspectors.DjangoRestResponsePagination", # Added - "drf_yasg.inspectors.DjangoRestResponsePagination", - "drf_yasg.inspectors.CoreAPICompatInspector", - ], -} + if env.OSFDB_HOST: DATABASES["osf"] = { "ENGINE": "django.db.backends.postgresql", From 67e1b8a5fe9a9242af51b1e16ea16e4fad130507 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 27 May 2025 16:09:33 +0300 Subject: [PATCH 082/100] fixed ssh url --- poetry.lock | 82 +++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/poetry.lock b/poetry.lock index 379dc305..fe435f50 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -113,7 +113,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -173,12 +173,12 @@ files = [ ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""] [[package]] name = "autobahn" @@ -199,13 +199,13 @@ setuptools = "*" txaio = ">=21.2.1" [package.extras] -all = ["PyGObject (>=3.40.0)", "argon2-cffi (>=20.1.0)", "attrs (>=20.3.0)", "base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "cffi (>=1.14.5)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "flatbuffers (>=22.12.6)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "msgpack (>=1.0.2)", "passlib (>=1.7.4)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "py-ubjson (>=0.16.1)", "pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "python-snappy (>=0.6.0)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "rlp (>=2.0.1)", "service-identity (>=18.1.0)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "twisted (>=24.3.0)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)", "zope.interface (>=5.2.0)"] +all = ["PyGObject (>=3.40.0)", "argon2-cffi (>=20.1.0)", "attrs (>=20.3.0)", "base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "cffi (>=1.14.5)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "flatbuffers (>=22.12.6)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "msgpack (>=1.0.2) ; platform_python_implementation == \"CPython\"", "passlib (>=1.7.4)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "py-ubjson (>=0.16.1)", "pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "python-snappy (>=0.6.0)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "rlp (>=2.0.1)", "service-identity (>=18.1.0)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "twisted (>=24.3.0)", "u-msgpack-python (>=2.1) ; platform_python_implementation != \"CPython\"", "ujson (>=4.0.2) ; platform_python_implementation == \"CPython\"", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)", "zope.interface (>=5.2.0)"] compress = ["python-snappy (>=0.6.0)"] -dev = ["backports.tempfile (>=1.0)", "build (>=1.2.1)", "bumpversion (>=0.5.3)", "codecov (>=2.0.15)", "flake8 (<5)", "humanize (>=0.5.1)", "mypy (>=0.610)", "passlib", "pep8-naming (>=0.3.3)", "pip (>=9.0.1)", "pyenchant (>=1.6.6)", "pyflakes (>=1.0.0)", "pyinstaller (>=4.2)", "pylint (>=1.9.2)", "pytest (>=3.4.2)", "pytest-aiohttp", "pytest-asyncio (>=0.14.0)", "pytest-runner (>=2.11.1)", "pyyaml (>=4.2b4)", "qualname", "sphinx (>=1.7.1)", "sphinx-autoapi (>=1.7.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-images (>=0.9.1)", "tox (>=4.2.8)", "tox-gh-actions (>=2.2.0)", "twine (>=3.3.0)", "twisted (>=22.10.0)", "txaio (>=20.4.1)", "watchdog (>=0.8.3)", "wheel (>=0.36.2)", "yapf (==0.29.0)"] +dev = ["backports.tempfile (>=1.0)", "build (>=1.2.1)", "bumpversion (>=0.5.3)", "codecov (>=2.0.15)", "flake8 (<5)", "humanize (>=0.5.1)", "mypy (>=0.610) ; python_version >= \"3.4\" and platform_python_implementation != \"PyPy\"", "passlib", "pep8-naming (>=0.3.3)", "pip (>=9.0.1)", "pyenchant (>=1.6.6)", "pyflakes (>=1.0.0)", "pyinstaller (>=4.2)", "pylint (>=1.9.2)", "pytest (>=3.4.2)", "pytest-aiohttp", "pytest-asyncio (>=0.14.0)", "pytest-runner (>=2.11.1)", "pyyaml (>=4.2b4)", "qualname", "sphinx (>=1.7.1)", "sphinx-autoapi (>=1.7.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-images (>=0.9.1)", "tox (>=4.2.8)", "tox-gh-actions (>=2.2.0)", "twine (>=3.3.0)", "twisted (>=22.10.0)", "txaio (>=20.4.1)", "watchdog (>=0.8.3)", "wheel (>=0.36.2)", "yapf (==0.29.0)"] encryption = ["pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "service-identity (>=18.1.0)"] nvx = ["cffi (>=1.14.5)"] scram = ["argon2-cffi (>=20.1.0)", "cffi (>=1.14.5)", "passlib (>=1.7.4)"] -serialization = ["cbor2 (>=5.2.0)", "flatbuffers (>=22.12.6)", "msgpack (>=1.0.2)", "py-ubjson (>=0.16.1)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)"] +serialization = ["cbor2 (>=5.2.0)", "flatbuffers (>=22.12.6)", "msgpack (>=1.0.2) ; platform_python_implementation == \"CPython\"", "py-ubjson (>=0.16.1)", "u-msgpack-python (>=2.1) ; platform_python_implementation != \"CPython\"", "ujson (>=4.0.2) ; platform_python_implementation == \"CPython\""] twisted = ["attrs (>=20.3.0)", "twisted (>=24.3.0)", "zope.interface (>=5.2.0)"] ui = ["PyGObject (>=3.40.0)"] xbr = ["base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "rlp (>=2.0.1)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)"] @@ -330,33 +330,33 @@ vine = ">=5.1.0,<6.0" arangodb = ["pyArango (>=2.0.2)"] auth = ["cryptography (==44.0.2)"] azureblockblob = ["azure-identity (>=1.19.0)", "azure-storage-blob (>=12.15.0)"] -brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] +brotli = ["brotli (>=1.0.0) ; platform_python_implementation == \"CPython\"", "brotlipy (>=0.7.0) ; platform_python_implementation == \"PyPy\""] cassandra = ["cassandra-driver (>=3.25.0,<4)"] consul = ["python-consul2 (==0.1.5)"] cosmosdbsql = ["pydocumentdb (==2.3.5)"] -couchbase = ["couchbase (>=3.0.0)"] +couchbase = ["couchbase (>=3.0.0) ; platform_python_implementation != \"PyPy\" and (platform_system != \"Windows\" or python_version < \"3.10\")"] couchdb = ["pycouchdb (==1.16.0)"] django = ["Django (>=2.2.28)"] dynamodb = ["boto3 (>=1.26.143)"] elasticsearch = ["elastic-transport (<=8.17.1)", "elasticsearch (<=8.17.2)"] -eventlet = ["eventlet (>=0.32.0)"] +eventlet = ["eventlet (>=0.32.0) ; python_version < \"3.10\""] gcs = ["google-cloud-firestore (==2.20.1)", "google-cloud-storage (>=2.10.0)", "grpcio (==1.67.0)"] gevent = ["gevent (>=1.5.0)"] -librabbitmq = ["librabbitmq (>=2.0.0)"] -memcache = ["pylibmc (==1.6.3)"] +librabbitmq = ["librabbitmq (>=2.0.0) ; python_version < \"3.11\""] +memcache = ["pylibmc (==1.6.3) ; platform_system != \"Windows\""] mongodb = ["pymongo (==4.10.1)"] msgpack = ["msgpack (==1.1.0)"] pydantic = ["pydantic (>=2.4)"] pymemcache = ["python-memcached (>=1.61)"] -pyro = ["pyro4 (==4.82)"] +pyro = ["pyro4 (==4.82) ; python_version < \"3.11\""] pytest = ["pytest-celery[all] (>=1.2.0,<1.3.0)"] redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] s3 = ["boto3 (>=1.26.143)"] slmq = ["softlayer_messaging (>=1.0.3)"] -solar = ["ephem (==4.2)"] +solar = ["ephem (==4.2) ; platform_python_implementation != \"PyPy\""] sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.4)", "urllib3 (>=1.26.16)"] -tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] +tblib = ["tblib (>=1.3.0) ; python_version < \"3.8.0\"", "tblib (>=1.5.0) ; python_version >= \"3.8.0\""] yaml = ["PyYAML (>=3.10)"] zookeeper = ["kazoo (>=1.3.1)"] zstd = ["zstandard (==0.23.0)"] @@ -718,10 +718,10 @@ files = [ cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version >= \"3.8\""] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""] +pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.1)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] @@ -961,9 +961,9 @@ drf-spectacular = ">=0.25.0" [package.source] type = "git" -url = "ssh://git@github.com/opaduchak/drf-spectacular-json-api.git" +url = "https://github.com/opaduchak/drf-spectacular-json-api.git" reference = "osf-fixes" -resolved_reference = "f280b852dbee1667c21a1998e7009567bfc113c4" +resolved_reference = "4a4b62908585fd7ab24e9f43ff4cb09dea4e049d" [[package]] name = "factory-boy" @@ -1014,7 +1014,7 @@ files = [ [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8)"] +typing = ["typing-extensions (>=4.8) ; python_version < \"3.11\""] [[package]] name = "flake8" @@ -1321,7 +1321,7 @@ azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6. confluentkafka = ["confluent-kafka (>=2.2.0)"] consul = ["python-consul2 (==0.1.5)"] gcpubsub = ["google-cloud-monitoring (>=2.16.0)", "google-cloud-pubsub (>=2.18.4)", "grpcio (==1.67.0)", "protobuf (==4.25.5)"] -librabbitmq = ["librabbitmq (>=2.0.0)"] +librabbitmq = ["librabbitmq (>=2.0.0) ; python_version < \"3.11\""] mongodb = ["pymongo (>=4.1.1)"] msgpack = ["msgpack (==1.1.0)"] pyro = ["pyro4 (==4.82)"] @@ -1779,8 +1779,8 @@ typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.2.6)"] -c = ["psycopg-c (==3.2.6)"] +binary = ["psycopg-binary (==3.2.6) ; implementation_name != \"pypy\""] +c = ["psycopg-c (==3.2.6) ; implementation_name != \"pypy\""] dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] @@ -2156,7 +2156,7 @@ requests = ">=2.30.0,<3.0" urllib3 = ">=1.25.10,<3.0" [package.extras] -tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli", "tomli-w", "types-PyYAML", "types-requests"] +tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli ; python_version < \"3.11\"", "tomli-w", "types-PyYAML", "types-requests"] [[package]] name = "rpds-py" @@ -2378,9 +2378,9 @@ files = [ ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6) ; python_version < \"3.10\"", "importlib-resources (>=5.10.2) ; python_version < \"3.9\"", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-ruff (<0.4) ; platform_system == \"Windows\"", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "pytest-ruff (>=0.3.2) ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -2457,19 +2457,19 @@ typing-extensions = ">=4.2.0" zope-interface = ">=5" [package.extras] -all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"] dev = ["coverage (>=7.5,<8.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "python-subunit (>=1.4,<2.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)"] dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"] -gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"] -macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] -mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"] -osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] -serial = ["pyserial (>=3.0)", "pywin32 (!=226)"] +macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"] +osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +serial = ["pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\""] test = ["cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"] tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"] -windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"] +windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"] [[package]] name = "txaio" @@ -2499,7 +2499,7 @@ files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -markers = {dev = "python_version < \"3.13\"", release = "python_version < \"3.13\""} +markers = {dev = "python_version == \"3.12\"", release = "python_version == \"3.12\""} [[package]] name = "tzdata" @@ -2539,7 +2539,7 @@ files = [ ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -2575,7 +2575,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "wcwidth" @@ -2741,4 +2741,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "b53f0026f04c386056607fc06558e28dcc265180e2cc7bf63cba00a78138c234" +content-hash = "995ab5635fe48f9e7c79a483b889bc8242022b1aec820b2a501992fb2ac648ce" diff --git a/pyproject.toml b/pyproject.toml index 5fb51e3d..af51bd52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ tqdm = "^4.67.1" itsdangerous = "^2.2.0" redis = "^5.2.1" drf-spectacular = "^0.28.0" -drf-spectacular-jsonapi = {git = "git@github.com:opaduchak/drf-spectacular-json-api.git", branch = "osf-fixes"} +drf-spectacular-jsonapi = {git = "https://github.com/opaduchak/drf-spectacular-json-api.git", branch = "osf-fixes"} [tool.poetry.group.dev.dependencies] From d8b9899cfb37873d93814aee2a816e5729636008 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 27 May 2025 18:27:42 +0300 Subject: [PATCH 083/100] fixed filters --- addon_service/apps.py | 4 ++++ addon_service/common/openapi_extensions.py | 28 ++++++++++++++++++++++ addon_service/common/viewsets.py | 3 +-- app/settings.py | 6 ----- 4 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 addon_service/common/openapi_extensions.py diff --git a/addon_service/apps.py b/addon_service/apps.py index 21e574e3..c2916203 100644 --- a/addon_service/apps.py +++ b/addon_service/apps.py @@ -3,3 +3,7 @@ class AddonServiceConfig(AppConfig): name = "addon_service" + + def ready(self): + # need to import openapi extensions here for them to be registered + import addon_service.common.openapi_extensions # noqa: F401 diff --git a/addon_service/common/openapi_extensions.py b/addon_service/common/openapi_extensions.py new file mode 100644 index 00000000..7c2f4498 --- /dev/null +++ b/addon_service/common/openapi_extensions.py @@ -0,0 +1,28 @@ +from drf_spectacular.extensions import OpenApiFilterExtension + + +class RestrictedReadOnlyViewSetExtension(OpenApiFilterExtension): + target_class = "addon_service.common.filtering.RestrictedListEndpointFilterBackend" # full dotted path + + def get_schema_operation_parameters(self, auto_schema, *args, **kwargs): + if auto_schema.method != "GET" or "list" not in auto_schema.view.action: + return [] + + required_filter_fields = getattr( + auto_schema.view, "required_list_filter_fields", () + ) + + parameters = [] + for field_name in required_filter_fields: + parameters.append( + { + "name": f"filter[{field_name}]", + "in": "query", # This corresponds to OpenApiParameter.QUERY + "description": f"Filter by {field_name}. This filter must be uniquely identifying.", + "required": True, + "schema": { + "type": "string" # This corresponds to OpenApiTypes.STR, OpenApiTypes.URI, etc. + }, + } + ) + return parameters diff --git a/addon_service/common/viewsets.py b/addon_service/common/viewsets.py index 4f20b36f..4fb386a9 100644 --- a/addon_service/common/viewsets.py +++ b/addon_service/common/viewsets.py @@ -24,6 +24,7 @@ class _DrfJsonApiHelpers(AutoPrefetchMixin, PreloadIncludesMixin, RelatedMixin): class RestrictedReadOnlyViewSet(ReadOnlyModelViewSet): + filter_backends = [RestrictedListEndpointFilterBackend] """ReadOnlyViewSet that requires `list` actions return only one result. UserReference and ResourceReference endpoints are major entry points into @@ -47,8 +48,6 @@ def list(self, request, *args, **kwargs): RestrictedListEndpointFilterBackend and check_object_permissions to enforce permissions on returned entities. """ - self.filter_backends = [RestrictedListEndpointFilterBackend] - qs = self.filter_queryset(self.get_queryset()) try: self.check_object_permissions(self.request, qs.get()) diff --git a/app/settings.py b/app/settings.py index fada488a..3e5dedad 100644 --- a/app/settings.py +++ b/app/settings.py @@ -209,7 +209,6 @@ "addon_service.common.queryparams_filter.AllowedQueryParamsFilter", "rest_framework_json_api.filters.OrderingFilter", "rest_framework_json_api.django_filters.DjangoFilterBackend", - "rest_framework.filters.SearchFilter", ), "DEFAULT_AUTHENTICATION_CLASSES": ( "addon_service.authentication.GVCombinedAuthentication", @@ -228,11 +227,6 @@ "SERVE_INCLUDE_SCHEMA": False, "COMPONENT_SPLIT_REQUEST": True, "PREPROCESSING_HOOKS": ["drf_spectacular_jsonapi.hooks.fix_nested_path_parameters"], - "EXTENSIONS_INFO": { - "drf_spectacular_jsonapi.extensions.JSONAPIResourceExtension": {}, - "drf_spectacular_jsonapi.extensions.JSONAPIErrorExtension": {}, - }, - # OTHER SETTINGS } # Password validation # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators From 51fe7f62ae70e8d1ed0f6dbdd5d678e4a2db8aaf Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 23 Jun 2025 13:41:16 +0300 Subject: [PATCH 084/100] fixed resurse reference and user reference endpoints --- addon_service/common/filtering.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addon_service/common/filtering.py b/addon_service/common/filtering.py index 84c7435b..e971d728 100644 --- a/addon_service/common/filtering.py +++ b/addon_service/common/filtering.py @@ -21,6 +21,8 @@ def filter_queryset(self, request, queryset, view): class RestrictedListEndpointFilterBackend(filters.BaseFilterBackend): def filter_queryset(self, request, queryset, view): + if view.action != "list": + return queryset required_filters = set(view.required_list_filter_fields) filter_expressions = extract_filter_expressions( request.query_params, view.get_serializer() From b8318738934cdd9cd11410d329e09f9d54bf4518 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Thu, 26 Jun 2025 16:30:10 +0300 Subject: [PATCH 085/100] added extend_schema descriptions, excluded internal views, fixed some viewsets to remove unused methods --- Dockerfile | 4 +- .../addon_operation_invocation/views.py | 18 ++++++- .../authorized_account/citation/views.py | 10 ++++ .../authorized_account/computing/views.py | 10 ++++ .../authorized_account/link/views.py | 10 ++++ .../authorized_account/storage/views.py | 10 ++++ addon_service/common/schemas.py | 47 +++++++++++++++++++ addon_service/common/viewsets.py | 11 +++++ .../configured_addon/citation/views.py | 16 +++++++ .../configured_addon/computing/models.py | 2 +- .../configured_addon/computing/views.py | 16 +++++++ addon_service/configured_addon/link/views.py | 15 ++++++ addon_service/configured_addon/models.py | 2 +- addon_service/configured_addon/serializers.py | 2 +- .../configured_addon/storage/views.py | 13 +++++ .../external_service/citation/views.py | 12 +++++ .../external_service/computing/views.py | 12 +++++ addon_service/external_service/link/views.py | 12 +++++ addon_service/external_service/models.py | 2 +- .../external_service/storage/views.py | 13 +++++ .../commands/fill_external_services.py | 16 ++++++- addon_service/oauth1/views.py | 2 + addon_service/oauth2/views.py | 2 + addon_service/resource_reference/views.py | 13 +++++ addon_service/user_reference/views.py | 13 +++++ 25 files changed, 274 insertions(+), 9 deletions(-) create mode 100644 addon_service/common/schemas.py diff --git a/Dockerfile b/Dockerfile index 04590361..39d84b18 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM python:3.13 AS gv-base # System Dependencies: -RUN apt-get update && apt-get install -y libpq-dev +RUN apt-get update && apt-get install -y libpq-dev libxml2-dev libxslt-dev COPY pyproject.toml /code/ COPY poetry.lock /code/ @@ -15,7 +15,7 @@ ENV PATH="$PATH:/root/.local/bin" FROM python:3.13-slim AS gv-runtime-base # System Dependencies: -RUN apt-get update && apt-get install -y libpq-dev +RUN apt-get update && apt-get install -y libpq-dev libxml2-dev libxslt-dev COPY pyproject.toml /code/ COPY poetry.lock /code/ diff --git a/addon_service/addon_operation_invocation/views.py b/addon_service/addon_operation_invocation/views.py index 579c44f7..2faaae8d 100644 --- a/addon_service/addon_operation_invocation/views.py +++ b/addon_service/addon_operation_invocation/views.py @@ -1,3 +1,7 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) from rest_framework.response import Response from addon_service.common.permissions import ( @@ -6,7 +10,7 @@ SessionUserMayAccessInvocation, SessionUserMayPerformInvocation, ) -from addon_service.common.viewsets import RetrieveWriteViewSet +from addon_service.common.viewsets import RetrieveCreateViewSet from addon_service.tasks.invocation import ( perform_invocation__blocking, perform_invocation__celery, @@ -31,7 +35,16 @@ from .serializers import AddonOperationInvocationSerializer -class AddonOperationInvocationViewSet(RetrieveWriteViewSet): +@extend_schema_view( + create=extend_schema( + description="Perform some action using external service, for instance list files on storage provider. " + "In order to perform such action you need to include configured_addon relationship" + ), + retrieve=extend_schema( + description="Get singular instance of addon operation invocation by it's pk. May be useful to view action log", + ), +) +class AddonOperationInvocationViewSet(RetrieveCreateViewSet): queryset = AddonOperationInvocation.objects.all() serializer_class = AddonOperationInvocationSerializer @@ -40,6 +53,7 @@ def get_permissions(self): case "retrieve" | "retrieve_related": return [IsAuthenticated(), SessionUserMayAccessInvocation()] case "partial_update" | "update" | "destroy": + # prohibit this? Maybe allow only to delete invocation from action log, but definitely not update return [IsAuthenticated(), SessionUserIsOwner()] case "create": return [SessionUserMayPerformInvocation()] diff --git a/addon_service/authorized_account/citation/views.py b/addon_service/authorized_account/citation/views.py index 28d3c837..452b9b1b 100644 --- a/addon_service/authorized_account/citation/views.py +++ b/addon_service/authorized_account/citation/views.py @@ -1,9 +1,19 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) + from addon_service.authorized_account.views import AuthorizedAccountViewSet from .models import AuthorizedCitationAccount from .serializers import AuthorizedCitationAccountSerializer +@extend_schema_view( + create=extend_schema( + description='Create new authorized citation account for given external citation service.\n For OAuth services it\'s required to create account with `"initiate_oauth"=true` in order to proceed with OAuth flow' + ), +) class AuthorizedCitationAccountViewSet(AuthorizedAccountViewSet): queryset = AuthorizedCitationAccount.objects.all() serializer_class = AuthorizedCitationAccountSerializer diff --git a/addon_service/authorized_account/computing/views.py b/addon_service/authorized_account/computing/views.py index 51f82168..d986cd2f 100644 --- a/addon_service/authorized_account/computing/views.py +++ b/addon_service/authorized_account/computing/views.py @@ -1,9 +1,19 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) + from addon_service.authorized_account.views import AuthorizedAccountViewSet from .models import AuthorizedComputingAccount from .serializers import AuthorizedComputingAccountSerializer +@extend_schema_view( + create=extend_schema( + description='Create new authorized computing account for given external computing service.\n For OAuth services it\'s required to create account with `"initiate_oauth"=true` in order to proceed with OAuth flow' + ), +) class AuthorizedComputingAccountViewSet(AuthorizedAccountViewSet): queryset = AuthorizedComputingAccount.objects.all() serializer_class = AuthorizedComputingAccountSerializer diff --git a/addon_service/authorized_account/link/views.py b/addon_service/authorized_account/link/views.py index c6ea9ab6..3f0440cb 100644 --- a/addon_service/authorized_account/link/views.py +++ b/addon_service/authorized_account/link/views.py @@ -1,9 +1,19 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) + from addon_service.authorized_account.views import AuthorizedAccountViewSet from .models import AuthorizedLinkAccount from .serializers import AuthorizedLinkAccountSerializer +@extend_schema_view( + create=extend_schema( + description='Create new authorized link account for given external link service.\n For OAuth services it\'s required to create account with `"initiate_oauth"=true` in order to proceed with OAuth flow' + ), +) class AuthorizedLinkAccountViewSet(AuthorizedAccountViewSet): queryset = AuthorizedLinkAccount.objects.all() serializer_class = AuthorizedLinkAccountSerializer diff --git a/addon_service/authorized_account/storage/views.py b/addon_service/authorized_account/storage/views.py index 587a03a0..fb66fe73 100644 --- a/addon_service/authorized_account/storage/views.py +++ b/addon_service/authorized_account/storage/views.py @@ -1,3 +1,8 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) + from addon_imps.storage.google_drive import GoogleDriveStorageImp from addon_service.authorized_account.views import AuthorizedAccountViewSet @@ -8,6 +13,11 @@ ) +@extend_schema_view( + create=extend_schema( + description='Create new authorized storage account for given external storage service.\n For OAuth services it\'s required to create account with `"initiate_oauth"=true` in order to proceed with OAuth flow' + ), +) class AuthorizedStorageAccountViewSet(AuthorizedAccountViewSet): queryset = AuthorizedStorageAccount.objects.all() serializer_class = AuthorizedStorageAccountSerializer diff --git a/addon_service/common/schemas.py b/addon_service/common/schemas.py new file mode 100644 index 00000000..4d7ab6d7 --- /dev/null +++ b/addon_service/common/schemas.py @@ -0,0 +1,47 @@ +from rest_framework.decorators import action +from rest_framework_json_api.relations import ResourceRelatedField + + +def auto_related_actions(cls): + """ + A class decorator that automatically adds a DRF @action for each + relationship on a ViewSet's serializer_class. + """ + # Find the serializer + serializer_class = getattr(cls, "serializer_class", None) + if not serializer_class: + return cls + + # Find all relationship fields + relationship_fields = { + field_name: field + for field_name, field in serializer_class().get_fields().items() + if isinstance(field, ResourceRelatedField) + } + + for field_name, field in relationship_fields.items(): + # This is the handler that will be used for our action. + # It's a bridge to the existing `retrieve_related` method. + def related_field_handler(self, request, *args, **kwargs): + # We pass the field_name explicitly to retrieve_related + kwargs["related_field"] = field_name + return self.retrieve_related(request, *args, **kwargs) + + # Set docstrings for better schema descriptions + related_field_handler.__doc__ = ( + f"Retrieve the related {field_name} for this resource." + ) + related_field_handler.__name__ = f"{field_name}_related_action" + + # Decorate our handler with @action. This is what the router looks for. + # The `url_path` will be the same as the field name. + decorated_handler = action( + detail=True, + methods=["get"], + url_path=field_name, + )(related_field_handler) + + # Attach the brand new, decorated method to the ViewSet class + setattr(cls, f"{field_name}_related_action", decorated_handler) + + return cls diff --git a/addon_service/common/viewsets.py b/addon_service/common/viewsets.py index 4fb386a9..d2ce9ca5 100644 --- a/addon_service/common/viewsets.py +++ b/addon_service/common/viewsets.py @@ -73,6 +73,17 @@ def perform_create(self, serializer): _instance.save() +class RetrieveCreateViewSet( + _DrfJsonApiHelpers, + _CreateWithPermissionsMixin, + drf_mixins.RetrieveModelMixin, + GenericViewSet, +): + """viewset allowing create, retrieve, update""" + + http_method_names = ["get", "post", "head", "options"] + + class RetrieveWriteViewSet( _DrfJsonApiHelpers, _CreateWithPermissionsMixin, diff --git a/addon_service/configured_addon/citation/views.py b/addon_service/configured_addon/citation/views.py index 936821a0..1982a702 100644 --- a/addon_service/configured_addon/citation/views.py +++ b/addon_service/configured_addon/citation/views.py @@ -1,9 +1,25 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) + from addon_service.configured_addon.views import ConfiguredAddonViewSet from .models import ConfiguredCitationAddon from .serializers import ConfiguredCitationAddonSerializer +@extend_schema_view( + create=extend_schema( + description="Create new configured citation addon for given authorized citation account, linking it to desired project.\n " + "To configure it properly, you must specify `root_folder` on the provider's side.\n " + "Note that everything under this folder is going to be accessible to everyone who has access to this project" + ), + get=extend_schema( + description="Get configured citation addon by it's pk. " + "\nIf you want to fetch all configured citation addons, you should do so through resource_reference related view", + ), +) class ConfiguredCitationAddonViewSet(ConfiguredAddonViewSet): queryset = ConfiguredCitationAddon.objects.active() serializer_class = ConfiguredCitationAddonSerializer diff --git a/addon_service/configured_addon/computing/models.py b/addon_service/configured_addon/computing/models.py index 25c96e93..88e2d848 100644 --- a/addon_service/configured_addon/computing/models.py +++ b/addon_service/configured_addon/computing/models.py @@ -18,6 +18,6 @@ def config(self) -> ComputingConfig: return self.base_account.authorizedcomputingaccount.config @property - def external_service_name(self): + def external_service_name(self) -> str: number = self.base_account.external_service.int_addon_imp return AddonImpNumbers(number).name.lower() diff --git a/addon_service/configured_addon/computing/views.py b/addon_service/configured_addon/computing/views.py index a267b932..c8e82b35 100644 --- a/addon_service/configured_addon/computing/views.py +++ b/addon_service/configured_addon/computing/views.py @@ -1,5 +1,9 @@ from http import HTTPMethod +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) from rest_framework.decorators import action from rest_framework.response import Response @@ -10,6 +14,18 @@ from .serializers import ConfiguredComputingAddonSerializer +@extend_schema_view( + create=extend_schema( + description="Create new configured computing addon for given authorized computing account, linking it to desired project.\n " + "To configure it properly, you must specify `root_folder` on the provider's side.\n " + "Note that everything under this folder is going to be accessible to everyone who has access to this project" + ), + get=extend_schema( + description="Get configured computing addon by it's pk. " + "\nIf you want to fetch all configured computing addons, you should do so through resource_reference related view", + ), + get_wb_credentials=extend_schema(exclude=True), +) class ConfiguredComputingAddonViewSet(ConfiguredAddonViewSet): queryset = ConfiguredComputingAddon.objects.active() serializer_class = ConfiguredComputingAddonSerializer diff --git a/addon_service/configured_addon/link/views.py b/addon_service/configured_addon/link/views.py index bf1a886b..b5c39dfd 100644 --- a/addon_service/configured_addon/link/views.py +++ b/addon_service/configured_addon/link/views.py @@ -1,5 +1,9 @@ from http import HTTPMethod +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) from rest_framework.decorators import action from rest_framework.response import Response @@ -13,6 +17,17 @@ ) +@extend_schema_view( + create=extend_schema( + description="Create new configured link addon for given authorized link account, linking it to desired project.\n " + "To configure it properly, you must specify `root_folder` on the provider's side.\n " + "Note that everything under this folder is going to be accessible to everyone who has access to this project" + ), + get=extend_schema( + description="Get configured link addon by it's pk. " + "\nIf you want to fetch all configured link addons, you should do so through resource_reference related view", + ), +) class ConfiguredLinkAddonViewSet(ConfiguredAddonViewSet): queryset = ConfiguredLinkAddon.objects.active().select_related( "base_account__authorizedlinkaccount", diff --git a/addon_service/configured_addon/models.py b/addon_service/configured_addon/models.py index ce91544c..9b6e759c 100644 --- a/addon_service/configured_addon/models.py +++ b/addon_service/configured_addon/models.py @@ -138,7 +138,7 @@ def imp_cls(self) -> type[AddonImp]: return self.base_account.imp_cls @property - def external_service_name(self): + def external_service_name(self) -> str: number = self.base_account.external_service.int_addon_imp return AddonImpNumbers(number).name.lower() diff --git a/addon_service/configured_addon/serializers.py b/addon_service/configured_addon/serializers.py index ded2328e..1b3b9768 100644 --- a/addon_service/configured_addon/serializers.py +++ b/addon_service/configured_addon/serializers.py @@ -33,7 +33,7 @@ def __init__(self, *args, **kwargs): current_user_is_owner = serializers.SerializerMethodField() - def get_current_user_is_owner(self, configured_addon: ConfiguredAddon): + def get_current_user_is_owner(self, configured_addon: ConfiguredAddon) -> bool: return configured_addon.owner_uri == get_user_uri(self.context["request"]) def create(self, validated_data): diff --git a/addon_service/configured_addon/storage/views.py b/addon_service/configured_addon/storage/views.py index f3e119d7..f128638e 100644 --- a/addon_service/configured_addon/storage/views.py +++ b/addon_service/configured_addon/storage/views.py @@ -1,5 +1,6 @@ from http import HTTPMethod +import drf_spectacular.utils from django.http import Http404 from rest_framework.decorators import action from rest_framework.response import Response @@ -14,6 +15,18 @@ from .serializers import ConfiguredStorageAddonSerializer +@drf_spectacular.utils.extend_schema_view( + create=drf_spectacular.utils.extend_schema( + description="Create new configured storage addon for given authorized storage account, linking it to desired project.\n " + "To configure it properly, you must specify `root_folder` on the provider's side.\n " + "Note that everything under this folder is going to be accessible to everyone who has access to this project" + ), + get=drf_spectacular.utils.extend_schema( + description="Get configured storage addon by it's pk. " + "\nIf you want to fetch all configured storage addons, you should do so through resource_reference related view", + ), + get_wb_credentials=drf_spectacular.utils.extend_schema(exclude=True), +) class ConfiguredStorageAddonViewSet(ConfiguredAddonViewSet): queryset = ConfiguredStorageAddon.objects.active().select_related( "base_account__authorizedstorageaccount", diff --git a/addon_service/external_service/citation/views.py b/addon_service/external_service/citation/views.py index b8e9fa03..cdbc7ca3 100644 --- a/addon_service/external_service/citation/views.py +++ b/addon_service/external_service/citation/views.py @@ -1,9 +1,21 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) from rest_framework_json_api.views import ReadOnlyModelViewSet from .models import ExternalCitationService from .serializers import ExternalCitationServiceSerializer +@extend_schema_view( + list=extend_schema( + description="Get the list of all available external citation services" + ), + get=extend_schema( + description="Get particular external citation service", + ), +) class ExternalCitationServiceViewSet(ReadOnlyModelViewSet): queryset = ExternalCitationService.objects.all().select_related( "oauth2_client_config", "oauth1_client_config" diff --git a/addon_service/external_service/computing/views.py b/addon_service/external_service/computing/views.py index b52baf0e..bc793b8d 100644 --- a/addon_service/external_service/computing/views.py +++ b/addon_service/external_service/computing/views.py @@ -1,9 +1,21 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) from rest_framework_json_api.views import ReadOnlyModelViewSet from .models import ExternalComputingService from .serializers import ExternalComputingServiceSerializer +@extend_schema_view( + list=extend_schema( + description="Get the list of all available external computing services" + ), + get=extend_schema( + description="Get particular external computing service", + ), +) class ExternalComputingServiceViewSet(ReadOnlyModelViewSet): queryset = ExternalComputingService.objects.all().select_related( "oauth2_client_config" diff --git a/addon_service/external_service/link/views.py b/addon_service/external_service/link/views.py index 6eccaf8d..8076a197 100644 --- a/addon_service/external_service/link/views.py +++ b/addon_service/external_service/link/views.py @@ -1,9 +1,21 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) from rest_framework_json_api.views import ReadOnlyModelViewSet from .models import ExternalLinkService from .serializers import ExternalLinkServiceSerializer +@extend_schema_view( + list=extend_schema( + description="Get the list of all available external link services" + ), + get=extend_schema( + description="Get particular external link service", + ), +) class ExternalLinkServiceViewSet(ReadOnlyModelViewSet): queryset = ExternalLinkService.objects.all().select_related("oauth2_client_config") serializer_class = ExternalLinkServiceSerializer diff --git a/addon_service/external_service/models.py b/addon_service/external_service/models.py index 7a7c3302..a371393a 100644 --- a/addon_service/external_service/models.py +++ b/addon_service/external_service/models.py @@ -90,7 +90,7 @@ def configurable_api_root(self): return ServiceTypes.HOSTED in self.service_type @property - def external_service_name(self): + def external_service_name(self) -> str: number = self.int_addon_imp return known_imps.AddonImpNumbers(number).name.lower() diff --git a/addon_service/external_service/storage/views.py b/addon_service/external_service/storage/views.py index 703f48c2..2bc4a1e2 100644 --- a/addon_service/external_service/storage/views.py +++ b/addon_service/external_service/storage/views.py @@ -1,9 +1,22 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) from rest_framework_json_api.views import ReadOnlyModelViewSet from .models import ExternalStorageService from .serializers import ExternalStorageServiceSerializer +@extend_schema_view( + list=extend_schema( + description="Get the list of all available external storage services" + ), + get=extend_schema( + description="Get particular external storage service", + ), + get_wb_credentials=extend_schema(exclude=True), +) class ExternalStorageServiceViewSet(ReadOnlyModelViewSet): queryset = ExternalStorageService.objects.all().select_related( "oauth2_client_config" diff --git a/addon_service/management/commands/fill_external_services.py b/addon_service/management/commands/fill_external_services.py index d4de9690..dd074a43 100644 --- a/addon_service/management/commands/fill_external_services.py +++ b/addon_service/management/commands/fill_external_services.py @@ -1,4 +1,5 @@ import csv +import logging from pathlib import Path from typing import Any @@ -7,6 +8,7 @@ from addon_service.external_service.citation.models import ExternalCitationService from addon_service.external_service.computing.models import ExternalComputingService from addon_service.external_service.link.models import ExternalLinkService +from addon_service.external_service.models import ExternalService from addon_service.external_service.storage.models import ExternalStorageService from addon_service.oauth1 import OAuth1ClientConfig from addon_service.oauth2 import OAuth2ClientConfig @@ -25,11 +27,13 @@ } icons_path = Path(__file__).parent.parent.parent / "static/provider_icons" +logger = logging.getLogger(__name__) class Command(BaseCommand): def handle(self, *args, **kwargs): services = self.load_csv_data(services_csv) + oauth2_configs = self.load_csv_data(oauth2_csv) oauth1_configs = self.load_csv_data(oauth1_csv) @@ -44,14 +48,24 @@ def handle(self, *args, **kwargs): oauth1_config = oauth1_configs[oauth1_id] oauth1_config = OAuth1ClientConfig.objects.create(**oauth1_config) service["oauth1_client_config"] = oauth1_config + logger.warning(f"Filled service {service['display_name']}") model.objects.create(**service) def load_csv_data(self, path: Path) -> dict[str, dict[str, Any]]: + current_services_names = { + service[0] + for service in ExternalService.objects.values_list("display_name").all() + } with path.open() as services_file: item = csv.reader(services_file) field_names = next(item) data_list = [dict(zip(field_names, service)) for service in item] - return {item.pop("id"): self.fix_values(item) for item in data_list} + print(data_list) + return { + item.pop("id"): self.fix_values(item) + for item in data_list + if item.get("display_name") not in current_services_names + } def fix_values(self, item): raw_values = {key: self.fix_value(value) for key, value in item.items()} diff --git a/addon_service/oauth1/views.py b/addon_service/oauth1/views.py index 9a72e367..423338d4 100644 --- a/addon_service/oauth1/views.py +++ b/addon_service/oauth1/views.py @@ -2,6 +2,7 @@ from asgiref.sync import async_to_sync from django.http import HttpResponse +from drf_spectacular.utils import extend_schema from addon_service.authorized_account.citation.models import AuthorizedCitationAccount from addon_service.authorized_account.computing.models import AuthorizedComputingAccount @@ -10,6 +11,7 @@ from addon_service.osf_models.fields import decrypt_string +@extend_schema(exclude=True) def oauth1_callback_view(request): oauth_token = request.GET["oauth_token"] oauth_verifier = request.GET["oauth_verifier"] diff --git a/addon_service/oauth2/views.py b/addon_service/oauth2/views.py index d442f676..8dc6623d 100644 --- a/addon_service/oauth2/views.py +++ b/addon_service/oauth2/views.py @@ -4,6 +4,7 @@ from asgiref.sync import sync_to_async from django.db import transaction from django.http import HttpResponse +from drf_spectacular.utils import extend_schema from addon_service.models import ( OAuth2ClientConfig, @@ -12,6 +13,7 @@ from addon_service.oauth2.utils import get_initial_access_token +@extend_schema(exclude=True) @transaction.non_atomic_requests # async views and ATOMIC_REQUESTS do not mix async def oauth2_callback_view(request): """ diff --git a/addon_service/resource_reference/views.py b/addon_service/resource_reference/views.py index 7d8eeec2..4f2e7a89 100644 --- a/addon_service/resource_reference/views.py +++ b/addon_service/resource_reference/views.py @@ -1,3 +1,8 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) + from addon_service.common.permissions import SessionUserCanViewReferencedResource from addon_service.common.viewsets import RestrictedReadOnlyViewSet from addon_service.serializers import ResourceReferenceSerializer @@ -5,6 +10,14 @@ from .models import ResourceReference +@extend_schema_view( + list=extend_schema( + description="Get resource reference by resource_uri. Even through this is a list method, this endpoint returns only one entity" + ), + retrieve=extend_schema( + description="Get resource reference by it's pk", + ), +) class ResourceReferenceViewSet(RestrictedReadOnlyViewSet): queryset = ResourceReference.objects.all() serializer_class = ResourceReferenceSerializer diff --git a/addon_service/user_reference/views.py b/addon_service/user_reference/views.py index f33b53af..d14dde3b 100644 --- a/addon_service/user_reference/views.py +++ b/addon_service/user_reference/views.py @@ -1,3 +1,8 @@ +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, +) + from addon_service.common.permissions import SessionUserIsOwner from addon_service.common.viewsets import RestrictedReadOnlyViewSet @@ -5,6 +10,14 @@ from .serializers import UserReferenceSerializer +@extend_schema_view( + list=extend_schema( + description="Get user reference by user_uri. Even through this is a list method, this endpoint returns only one entity" + ), + retrieve=extend_schema( + description="Get user reference by it's pk", + ), +) class UserReferenceViewSet(RestrictedReadOnlyViewSet): queryset = UserReference.objects.all() serializer_class = UserReferenceSerializer From ac47984c5c15671752855c0bff9c94955eaa686b Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 30 Jun 2025 16:29:01 +0300 Subject: [PATCH 086/100] added management command to generate proper openapi spec --- addon_service/management/commands/openapi.py | 201 +++++++++++++++++++ app/urls.py | 16 -- 2 files changed, 201 insertions(+), 16 deletions(-) create mode 100644 addon_service/management/commands/openapi.py diff --git a/addon_service/management/commands/openapi.py b/addon_service/management/commands/openapi.py new file mode 100644 index 00000000..377c1098 --- /dev/null +++ b/addon_service/management/commands/openapi.py @@ -0,0 +1,201 @@ +import copy +import os +import sys + +import yaml +from django.core.management import ( + BaseCommand, + call_command, +) +from yaml import ( + CSafeDumper, + CSafeLoader, +) + + +RELATED_FIELD_PARAM_NAME = "related_field" + +JSON_API_CONTENT_TYPE = "application/vnd.api+json" + + +def kebab_to_camel(s: str) -> str: + if s == "AuthorizedAccount": + return "AuthorizedStorageAccount" + elif s == "ConfiguredAddon": + return "ConfiguredStorageAddon" + s = s.title() + parts = s.split("-") + return parts[0] + "".join(word.capitalize() for word in parts[1:]) + + +def fix_resource(s: str) -> str: + if s == "AuthorizedAccount": + return "authorized-storage-account" + elif s == "ConfiguredAddon": + return "configured-storage-addon" + return s + + +def get_schema_from_ref(openapi_data, ref): + """ + Resolves a $ref string to its corresponding schema object in the OpenAPI data. + Example: '#/components/schemas/Article' -> openapi_data['components']['schemas']['Article'] + """ + parts = ref.strip("#/").split("/") + node = openapi_data + for part in parts: + if part in node: + node = node[part] + else: + return None + return node + + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( + "--output", + type=str, + default="openapi.yml", + ) + + def handle(self, *args, **options): + call_command("spectacular", file=".openapi.yml") + with open(".openapi.yml") as buf: + data = yaml.load(buf, CSafeLoader) + os.remove(".openapi.yml") + + if "paths" not in data: + print("Warning: No 'paths' object found in the YAML file. Nothing to do.") + yaml.dump(data, sys.stdout) + return + + paths = data["paths"] + new_paths = {} + paths_to_delete = [] + + print("🔍 Starting scan for generic relationship endpoints...") + reverse_path_index = { + path_item["get"]["operationId"].replace("_", "-"): path_item + for path, path_item in paths.items() + if "get" in path_item + } + + for path, path_item in paths.items(): + if path.endswith(f"/{{{RELATED_FIELD_PARAM_NAME}}}/"): + print(f" -> Found generic relationship path: {path}") + paths_to_delete.append(path) + + base_path = path.rsplit("/", 2)[0] + "/" + primary_resource_path = f"{base_path}" + + if primary_resource_path not in paths: + print( + f" [!] Warning: Could not find parent path '{primary_resource_path}' to infer relationships. Skipping." + ) + continue + + try: + # We assume the 'get' operation on the primary resource defines its schema + schema_ref: str = paths[primary_resource_path]["get"]["responses"][ + "200" + ]["content"][JSON_API_CONTENT_TYPE]["schema"]["$ref"] + primary_resource_schema = get_schema_from_ref( + data, schema_ref.removesuffix("Response") + ) + except KeyError: + print( + f" [!] Warning: Could not find a valid 200 OK schema reference for '{primary_resource_path}'. Skipping." + ) + continue + + if not primary_resource_schema: + print( + f" [!] Warning: Could not resolve schema reference '{schema_ref}'. Skipping." + ) + continue + + try: + relationships = primary_resource_schema["properties"][ + "relationships" + ]["properties"] + except KeyError: + print( + f" [!] Warning: No 'properties.relationships.properties' found in schema for '{primary_resource_path}'. Skipping." + ) + continue + + print(f" Found relationships: {', '.join(relationships.keys())}") + + for rel_name, rel_schema in relationships.items(): + new_path_str = f"{base_path}{rel_name}" + resource: str = rel_schema["properties"]["data"].get( + "properties", + rel_schema["properties"]["data"] + .get("items", {}) + .get("properties"), + )["type"]["enum"][0] + # if resource.endswith('s'): + resource = resource.removesuffix("s") + # schema_name = f'Paginated{kebab_to_camel(resource)}List' + # else: + schema_name = f"{kebab_to_camel(resource)}Response" + new_schema_ref = f"#/components/schemas/{schema_name}" + new_path_item = copy.deepcopy( + reverse_path_index.get(f"{fix_resource(resource)}s-retrieve") + ) + if not new_path_item: + new_path_item = copy.deepcopy(path_item) + + method = "get" + operation = new_path_item[method] + operation["operationId"] = ( + f"{path_item[method]['operationId']}_related_{rel_name}" + ) + operation["tags"] = path_item[method]["tags"] + if "parameters" in operation: + operation["parameters"] = [ + p + for p in operation["parameters"] + if p.get("name") != RELATED_FIELD_PARAM_NAME + ] + + try: + operation["responses"]["200"]["content"][JSON_API_CONTENT_TYPE][ + "schema" + ] = {"$ref": new_schema_ref} + print( + f" ✓ Creating endpoint '{method.upper()} {new_path_str}'" + ) + print(f" - Pointing schema to: {new_schema_ref}") + + except KeyError: + print( + f" [!] Warning: Could not find a valid response structure in '{method.upper()} {path}' to rewire the schema." + ) + + new_paths[new_path_str] = {"get": operation} + + output_file = options["output"] + if paths_to_delete: + print("\n🔄 Updating OpenAPI structure...") + for path in paths_to_delete: + del data["paths"][path] + print(f" - Removed generic path: {path}") + + data["paths"].update(new_paths) + for path in new_paths: + print(f" + Added specific path: {path}") + + # Dump the modified data to the output file + with open(output_file, "w") as f: + yaml.dump(data, f) + print(f"\n✅ Success! Wrote refined OpenAPI spec to '{output_file}'") + else: + print( + "\n✅ No generic relationship paths found to modify. Output file is unchanged." + ) + # Optionally write to output file anyway + with open(output_file, "w") as f: + yaml.dump(data, f, CSafeDumper) diff --git a/app/urls.py b/app/urls.py index 74285096..1e307e51 100644 --- a/app/urls.py +++ b/app/urls.py @@ -5,11 +5,6 @@ path, ) from django.views.generic.base import RedirectView -from drf_spectacular.views import ( - SpectacularAPIView, - SpectacularRedocView, - SpectacularSwaggerView, -) urlpatterns = [ @@ -20,17 +15,6 @@ RedirectView.as_view(url="/static/gravyvalet_code_docs/index.html"), name="docs-root", ), - path("api/schema/", SpectacularAPIView.as_view(), name="schema"), - path( - "api/schema/swagger-ui/", - SpectacularSwaggerView.as_view(url_name="schema"), - name="swagger-ui", - ), - path( - "api/schema/redoc/", - SpectacularRedocView.as_view(url_name="schema"), - name="redoc", - ), ] if "silk" in settings.INSTALLED_APPS: From e35d973eeca9ea62481690dbe06d4538dfc3d044 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 30 Jun 2025 16:33:29 +0300 Subject: [PATCH 087/100] reverted removal of swagger views, added debug switch --- app/urls.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/urls.py b/app/urls.py index 1e307e51..c0c0ef0a 100644 --- a/app/urls.py +++ b/app/urls.py @@ -5,6 +5,11 @@ path, ) from django.views.generic.base import RedirectView +from drf_spectacular.views import ( + SpectacularAPIView, + SpectacularRedocView, + SpectacularSwaggerView, +) urlpatterns = [ @@ -16,6 +21,18 @@ name="docs-root", ), ] +if settings.DEBUG: + path("api/schema/", SpectacularAPIView.as_view(), name="schema"), + path( + "api/schema/swagger-ui/", + SpectacularSwaggerView.as_view(url_name="schema"), + name="swagger-ui", + ), + path( + "api/schema/redoc/", + SpectacularRedocView.as_view(url_name="schema"), + name="redoc", + ), if "silk" in settings.INSTALLED_APPS: urlpatterns.append(path("silk/", include("silk.urls", namespace="silk"))) From 313937f7808cf0233c014d113b3f36405c31e8ad Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 30 Jun 2025 16:35:07 +0300 Subject: [PATCH 088/100] declutter --- addon_service/management/commands/openapi.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/addon_service/management/commands/openapi.py b/addon_service/management/commands/openapi.py index 377c1098..7db458e5 100644 --- a/addon_service/management/commands/openapi.py +++ b/addon_service/management/commands/openapi.py @@ -37,10 +37,6 @@ def fix_resource(s: str) -> str: def get_schema_from_ref(openapi_data, ref): - """ - Resolves a $ref string to its corresponding schema object in the OpenAPI data. - Example: '#/components/schemas/Article' -> openapi_data['components']['schemas']['Article'] - """ parts = ref.strip("#/").split("/") node = openapi_data for part in parts: @@ -97,7 +93,6 @@ def handle(self, *args, **options): continue try: - # We assume the 'get' operation on the primary resource defines its schema schema_ref: str = paths[primary_resource_path]["get"]["responses"][ "200" ]["content"][JSON_API_CONTENT_TYPE]["schema"]["$ref"] @@ -136,10 +131,7 @@ def handle(self, *args, **options): .get("items", {}) .get("properties"), )["type"]["enum"][0] - # if resource.endswith('s'): resource = resource.removesuffix("s") - # schema_name = f'Paginated{kebab_to_camel(resource)}List' - # else: schema_name = f"{kebab_to_camel(resource)}Response" new_schema_ref = f"#/components/schemas/{schema_name}" new_path_item = copy.deepcopy( @@ -188,7 +180,6 @@ def handle(self, *args, **options): for path in new_paths: print(f" + Added specific path: {path}") - # Dump the modified data to the output file with open(output_file, "w") as f: yaml.dump(data, f) print(f"\n✅ Success! Wrote refined OpenAPI spec to '{output_file}'") @@ -196,6 +187,5 @@ def handle(self, *args, **options): print( "\n✅ No generic relationship paths found to modify. Output file is unchanged." ) - # Optionally write to output file anyway with open(output_file, "w") as f: yaml.dump(data, f, CSafeDumper) From ad3cf3671b33c22e3b6fc3dfaf99912faade3a5d Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 30 Jun 2025 17:37:42 +0300 Subject: [PATCH 089/100] added generated names for relations --- addon_service/management/commands/openapi.py | 54 +- openapi.yml | 11403 +++++++++++++++++ 2 files changed, 11440 insertions(+), 17 deletions(-) create mode 100644 openapi.yml diff --git a/addon_service/management/commands/openapi.py b/addon_service/management/commands/openapi.py index 7db458e5..2753f0a7 100644 --- a/addon_service/management/commands/openapi.py +++ b/addon_service/management/commands/openapi.py @@ -47,6 +47,13 @@ def get_schema_from_ref(openapi_data, ref): return node +def make_decription(child_name: str, parent_name: str, relation_name: str): + if relation_name.endswith("s"): + return f"Fetch all related {kebab_to_camel(child_name)}s to this {parent_name}" + else: + return f"Fetch {parent_name}'s {kebab_to_camel(child_name)}" + + class Command(BaseCommand): def add_arguments(self, parser): @@ -78,30 +85,29 @@ def handle(self, *args, **options): if "get" in path_item } - for path, path_item in paths.items(): + for path, parent_path_item in paths.items(): if path.endswith(f"/{{{RELATED_FIELD_PARAM_NAME}}}/"): print(f" -> Found generic relationship path: {path}") paths_to_delete.append(path) base_path = path.rsplit("/", 2)[0] + "/" - primary_resource_path = f"{base_path}" - if primary_resource_path not in paths: + if base_path not in paths: print( - f" [!] Warning: Could not find parent path '{primary_resource_path}' to infer relationships. Skipping." + f" [!] Warning: Could not find parent path '{base_path}' to infer relationships. Skipping." ) continue try: - schema_ref: str = paths[primary_resource_path]["get"]["responses"][ - "200" - ]["content"][JSON_API_CONTENT_TYPE]["schema"]["$ref"] + schema_ref: str = paths[base_path]["get"]["responses"]["200"][ + "content" + ][JSON_API_CONTENT_TYPE]["schema"]["$ref"] primary_resource_schema = get_schema_from_ref( data, schema_ref.removesuffix("Response") ) except KeyError: print( - f" [!] Warning: Could not find a valid 200 OK schema reference for '{primary_resource_path}'. Skipping." + f" [!] Warning: Could not find a valid 200 OK schema reference for '{base_path}'. Skipping." ) continue @@ -117,7 +123,7 @@ def handle(self, *args, **options): ]["properties"] except KeyError: print( - f" [!] Warning: No 'properties.relationships.properties' found in schema for '{primary_resource_path}'. Skipping." + f" [!] Warning: No 'properties.relationships.properties' found in schema for '{base_path}'. Skipping." ) continue @@ -133,19 +139,26 @@ def handle(self, *args, **options): )["type"]["enum"][0] resource = resource.removesuffix("s") schema_name = f"{kebab_to_camel(resource)}Response" - new_schema_ref = f"#/components/schemas/{schema_name}" + relationship_schema_ref = f"#/components/schemas/{schema_name}" new_path_item = copy.deepcopy( reverse_path_index.get(f"{fix_resource(resource)}s-retrieve") ) - if not new_path_item: - new_path_item = copy.deepcopy(path_item) method = "get" operation = new_path_item[method] operation["operationId"] = ( - f"{path_item[method]['operationId']}_related_{rel_name}" + f"{parent_path_item[method]['operationId']}_related_{rel_name}" + ) + parent_name = schema_ref.rsplit("/", maxsplit=1)[1].removesuffix( + "Response" + ) + operation["description"] = ( + f"Fetch all related {kebab_to_camel(resource)}s to this {parent_name}" + ) + operation["description"] = make_decription( + resource, parent_name, rel_name ) - operation["tags"] = path_item[method]["tags"] + operation["tags"] = parent_path_item[method]["tags"] if "parameters" in operation: operation["parameters"] = [ p @@ -156,11 +169,13 @@ def handle(self, *args, **options): try: operation["responses"]["200"]["content"][JSON_API_CONTENT_TYPE][ "schema" - ] = {"$ref": new_schema_ref} + ] = {"$ref": relationship_schema_ref} print( f" ✓ Creating endpoint '{method.upper()} {new_path_str}'" ) - print(f" - Pointing schema to: {new_schema_ref}") + print( + f" - Pointing schema to: {relationship_schema_ref}" + ) except KeyError: print( @@ -169,7 +184,12 @@ def handle(self, *args, **options): new_paths[new_path_str] = {"get": operation} - output_file = options["output"] + self.collect_and_write_output( + data, new_paths, options["output"], paths_to_delete + ) + + @staticmethod + def collect_and_write_output(data, new_paths, output_file, paths_to_delete): if paths_to_delete: print("\n🔄 Updating OpenAPI structure...") for path in paths_to_delete: diff --git a/openapi.yml b/openapi.yml new file mode 100644 index 00000000..075b60ce --- /dev/null +++ b/openapi.yml @@ -0,0 +1,11403 @@ +components: + schemas: + AddonImp: + additionalProperties: false + properties: + attributes: + properties: + docstring: + readOnly: true + type: string + interface_docstring: + readOnly: true + type: string + name: + readOnly: true + type: string + type: object + id: {} + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + implemented_operations: + description: A related resource object from type addon-operations + properties: + data: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Implemented operations + type: object + type: object + type: + allOf: + - $ref: '#/components/schemas/AddonImpTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + - id + type: object + AddonImpResponse: + properties: + data: + items: + $ref: '#/components/schemas/AddonImp' + type: array + required: + - data + type: object + AddonImpTypeEnum: + enum: + - addon-imps + type: string + AddonOperation: + additionalProperties: false + properties: + attributes: + properties: + capability: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + readOnly: true + type: string + docstring: + readOnly: true + type: string + kwargs_jsonschema: + readOnly: true + name: + readOnly: true + type: string + result_jsonschema: + readOnly: true + type: object + id: {} + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + implemented_by: + description: A related resource object from type addon-imps + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-imps + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Implemented by + type: object + type: object + type: + allOf: + - $ref: '#/components/schemas/AddonOperationTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + - id + type: object + AddonOperationInvocation: + additionalProperties: false + properties: + attributes: + properties: + created: + format: date-time + readOnly: true + type: string + invocation_status: + description: '* `STARTING` - STARTING + + * `GOING` - GOING + + * `SUCCESS` - SUCCESS + + * `ERROR` - ERROR' + enum: + - STARTING + - GOING + - SUCCESS + - ERROR + readOnly: true + type: string + modified: + format: date-time + readOnly: true + type: string + operation_kwargs: {} + operation_name: + type: string + operation_result: + readOnly: true + required: + - operation_kwargs + - operation_name + type: object + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + by_user: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: By user + type: object + operation: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Operation + type: object + thru_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - AuthorizedAccount + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Thru account + type: object + thru_addon: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - ConfiguredAddon + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Thru addon + type: object + type: object + type: + allOf: + - $ref: '#/components/schemas/AddonOperationInvocationTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + type: object + AddonOperationInvocationRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + created: + format: date-time + readOnly: true + type: string + invocation_status: + description: '* `STARTING` - STARTING + + * `GOING` - GOING + + * `SUCCESS` - SUCCESS + + * `ERROR` - ERROR' + enum: + - STARTING + - GOING + - SUCCESS + - ERROR + readOnly: true + type: string + modified: + format: date-time + readOnly: true + type: string + operation_kwargs: {} + operation_name: + minLength: 1 + type: string + operation_result: + readOnly: true + required: + - operation_kwargs + - operation_name + type: object + relationships: + properties: + by_user: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: By user + type: object + operation: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Operation + type: object + thru_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - AuthorizedAccount + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Thru account + type: object + thru_addon: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - ConfiguredAddon + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Thru addon + type: object + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - addon-operation-invocations + type: string + required: + - type + type: object + required: + - data + type: object + AddonOperationInvocationResponse: + properties: + data: + $ref: '#/components/schemas/AddonOperationInvocation' + required: + - data + type: object + AddonOperationInvocationTypeEnum: + enum: + - addon-operation-invocations + type: string + AddonOperationResponse: + properties: + data: + items: + $ref: '#/components/schemas/AddonOperation' + type: array + required: + - data + type: object + AddonOperationTypeEnum: + enum: + - addon-operations + type: string + AuthorizedCitationAccount: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + default_root_folder: + type: string + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_citation_addons: + description: A related resource object from type configured-citation-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - configured-citation-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Configured citation addons + type: object + external_citation_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - external-citation-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: External citation service + type: object + required: + - external_citation_service + type: object + type: + allOf: + - $ref: '#/components/schemas/AuthorizedCitationAccountTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + type: object + AuthorizedCitationAccountRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + minLength: 1 + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + default_root_folder: + type: string + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_citation_addons: + description: A related resource object from type configured-citation-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - configured-citation-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + required: + - data + title: Configured citation addons + type: object + external_citation_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-citation-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: External citation service + type: object + required: + - external_citation_service + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - authorized-citation-accounts + type: string + required: + - type + type: object + required: + - data + type: object + AuthorizedCitationAccountResponse: + properties: + data: + $ref: '#/components/schemas/AuthorizedCitationAccount' + required: + - data + type: object + AuthorizedCitationAccountTypeEnum: + enum: + - authorized-citation-accounts + type: string + AuthorizedComputingAccount: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_computing_addons: + description: A related resource object from type configured-computing-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - configured-computing-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Configured computing addons + type: object + external_computing_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - external-computing-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: External computing service + type: object + required: + - external_computing_service + type: object + type: + allOf: + - $ref: '#/components/schemas/AuthorizedComputingAccountTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + type: object + AuthorizedComputingAccountRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + minLength: 1 + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_computing_addons: + description: A related resource object from type configured-computing-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - configured-computing-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + required: + - data + title: Configured computing addons + type: object + external_computing_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-computing-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: External computing service + type: object + required: + - external_computing_service + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - authorized-computing-accounts + type: string + required: + - type + type: object + required: + - data + type: object + AuthorizedComputingAccountResponse: + properties: + data: + $ref: '#/components/schemas/AuthorizedComputingAccount' + required: + - data + type: object + AuthorizedComputingAccountTypeEnum: + enum: + - authorized-computing-accounts + type: string + AuthorizedLinkAccount: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_link_addons: + description: A related resource object from type configured-link-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - configured-link-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Configured link addons + type: object + external_link_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - external-link-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: External link service + type: object + required: + - external_link_service + type: object + type: + allOf: + - $ref: '#/components/schemas/AuthorizedLinkAccountTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + type: object + AuthorizedLinkAccountRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + minLength: 1 + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_link_addons: + description: A related resource object from type configured-link-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - configured-link-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + required: + - data + title: Configured link addons + type: object + external_link_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-link-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: External link service + type: object + required: + - external_link_service + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - authorized-link-accounts + type: string + required: + - type + type: object + required: + - data + type: object + AuthorizedLinkAccountResponse: + properties: + data: + $ref: '#/components/schemas/AuthorizedLinkAccount' + required: + - data + type: object + AuthorizedLinkAccountTypeEnum: + enum: + - authorized-link-accounts + type: string + AuthorizedStorageAccount: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + default_root_folder: + type: string + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + external_storage_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - external-storage-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: External storage service + type: object + required: + - external_storage_service + type: object + type: + allOf: + - $ref: '#/components/schemas/AuthorizedStorageAccountTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + type: object + AuthorizedStorageAccountRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + minLength: 1 + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + default_root_folder: + type: string + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + external_storage_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-storage-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: External storage service + type: object + required: + - external_storage_service + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - authorized-storage-accounts + type: string + required: + - type + type: object + required: + - data + type: object + AuthorizedStorageAccountResponse: + properties: + data: + $ref: '#/components/schemas/AuthorizedStorageAccount' + required: + - data + type: object + AuthorizedStorageAccountTypeEnum: + enum: + - authorized-storage-accounts + type: string + ConfiguredCitationAddon: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + root_folder: + type: string + required: + - connected_capabilities + type: object + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - authorized-citation-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_citation_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - external-citation-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: External citation service + type: object + required: + - base_account + type: object + type: + allOf: + - $ref: '#/components/schemas/ConfiguredCitationAddonTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + type: object + ConfiguredCitationAddonRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + minLength: 1 + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + minLength: 1 + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + root_folder: + type: string + required: + - connected_capabilities + type: object + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - authorized-citation-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_citation_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-citation-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: External citation service + type: object + required: + - base_account + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - configured-citation-addons + type: string + required: + - type + type: object + required: + - data + type: object + ConfiguredCitationAddonResponse: + properties: + data: + $ref: '#/components/schemas/ConfiguredCitationAddon' + required: + - data + type: object + ConfiguredCitationAddonTypeEnum: + enum: + - configured-citation-addons + type: string + ConfiguredComputingAddon: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + required: + - connected_capabilities + type: object + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - authorized-computing-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_computing_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - external-computing-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: External computing service + type: object + required: + - base_account + type: object + type: + allOf: + - $ref: '#/components/schemas/ConfiguredComputingAddonTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + type: object + ConfiguredComputingAddonRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + minLength: 1 + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + minLength: 1 + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + required: + - connected_capabilities + type: object + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - authorized-computing-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_computing_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-computing-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: External computing service + type: object + required: + - base_account + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - configured-computing-addons + type: string + required: + - type + type: object + required: + - data + type: object + ConfiguredComputingAddonResponse: + properties: + data: + $ref: '#/components/schemas/ConfiguredComputingAddon' + required: + - data + type: object + ConfiguredComputingAddonTypeEnum: + enum: + - configured-computing-addons + type: string + ConfiguredLinkAddon: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + resource_type: + description: '* `Audiovisual` - Audiovisual + + * `Award` - Award + + * `Book` - Book + + * `BookChapter` - BookChapter + + * `Collection` - Collection + + * `ComputationalNotebook` - ComputationalNotebook + + * `ConferencePaper` - ConferencePaper + + * `ConferenceProceeding` - ConferenceProceeding + + * `DataPaper` - DataPaper + + * `Dataset` - Dataset + + * `Dissertation` - Dissertation + + * `Event` - Event + + * `Image` - Image + + * `Instrument` - Instrument + + * `InteractiveResource` - InteractiveResource + + * `Journal` - Journal + + * `JournalArticle` - JournalArticle + + * `Model` - Model + + * `OutputManagementPlan` - OutputManagementPlan + + * `PeerReview` - PeerReview + + * `PhysicalObject` - PhysicalObject + + * `Preprint` - Preprint + + * `Project` - Project + + * `Report` - Report + + * `Service` - Service + + * `Software` - Software + + * `Sound` - Sound + + * `Standard` - Standard + + * `StudyRegistration` - StudyRegistration + + * `Text` - Text + + * `Workflow` - Workflow + + * `Other` - Other' + enum: + - Audiovisual + - Award + - Book + - BookChapter + - Collection + - ComputationalNotebook + - ConferencePaper + - ConferenceProceeding + - DataPaper + - Dataset + - Dissertation + - Event + - Image + - Instrument + - InteractiveResource + - Journal + - JournalArticle + - Model + - OutputManagementPlan + - PeerReview + - PhysicalObject + - Preprint + - Project + - Report + - Service + - Software + - Sound + - Standard + - StudyRegistration + - Text + - Workflow + - Other + - null + nullable: true + type: string + target_id: + nullable: true + type: string + target_url: + format: uri + readOnly: true + type: string + required: + - connected_capabilities + type: object + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - authorized-link-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_link_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - external-link-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: External link service + type: object + required: + - base_account + type: object + type: + allOf: + - $ref: '#/components/schemas/ConfiguredLinkAddonTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + type: object + ConfiguredLinkAddonRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + minLength: 1 + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + resource_type: + description: '* `Audiovisual` - Audiovisual + + * `Award` - Award + + * `Book` - Book + + * `BookChapter` - BookChapter + + * `Collection` - Collection + + * `ComputationalNotebook` - ComputationalNotebook + + * `ConferencePaper` - ConferencePaper + + * `ConferenceProceeding` - ConferenceProceeding + + * `DataPaper` - DataPaper + + * `Dataset` - Dataset + + * `Dissertation` - Dissertation + + * `Event` - Event + + * `Image` - Image + + * `Instrument` - Instrument + + * `InteractiveResource` - InteractiveResource + + * `Journal` - Journal + + * `JournalArticle` - JournalArticle + + * `Model` - Model + + * `OutputManagementPlan` - OutputManagementPlan + + * `PeerReview` - PeerReview + + * `PhysicalObject` - PhysicalObject + + * `Preprint` - Preprint + + * `Project` - Project + + * `Report` - Report + + * `Service` - Service + + * `Software` - Software + + * `Sound` - Sound + + * `Standard` - Standard + + * `StudyRegistration` - StudyRegistration + + * `Text` - Text + + * `Workflow` - Workflow + + * `Other` - Other' + enum: + - Audiovisual + - Award + - Book + - BookChapter + - Collection + - ComputationalNotebook + - ConferencePaper + - ConferenceProceeding + - DataPaper + - Dataset + - Dissertation + - Event + - Image + - Instrument + - InteractiveResource + - Journal + - JournalArticle + - Model + - OutputManagementPlan + - PeerReview + - PhysicalObject + - Preprint + - Project + - Report + - Service + - Software + - Sound + - Standard + - StudyRegistration + - Text + - Workflow + - Other + - null + nullable: true + type: string + target_id: + minLength: 1 + nullable: true + type: string + target_url: + format: uri + minLength: 1 + readOnly: true + type: string + required: + - connected_capabilities + type: object + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - authorized-link-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_link_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-link-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: External link service + type: object + required: + - base_account + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - configured-link-addons + type: string + required: + - type + type: object + required: + - data + type: object + ConfiguredLinkAddonResponse: + properties: + data: + $ref: '#/components/schemas/ConfiguredLinkAddon' + required: + - data + type: object + ConfiguredLinkAddonTypeEnum: + enum: + - configured-link-addons + type: string + ConfiguredStorageAddon: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + root_folder: + type: string + required: + - connected_capabilities + type: object + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - authorized-storage-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_storage_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - external-storage-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + readOnly: true + required: + - data + title: External storage service + type: object + required: + - base_account + type: object + type: + allOf: + - $ref: '#/components/schemas/ConfiguredStorageAddonTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + type: object + ConfiguredStorageAddonRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + minLength: 1 + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + root_folder: + type: string + required: + - connected_capabilities + type: object + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - authorized-storage-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_storage_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-storage-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: External storage service + type: object + required: + - base_account + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - configured-storage-addons + type: string + required: + - type + type: object + required: + - data + type: object + ConfiguredStorageAddonResponse: + properties: + data: + $ref: '#/components/schemas/ConfiguredStorageAddon' + required: + - data + type: object + ConfiguredStorageAddonTypeEnum: + enum: + - configured-storage-addons + type: string + ExternalCitationService: + additionalProperties: false + properties: + attributes: + properties: + api_base_url_options: + items: + type: string + readOnly: true + type: array + auth_uri: + readOnly: true + type: string + configurable_api_root: + readOnly: true + type: string + credentials_format: + description: '* `UNSPECIFIED` - UNSPECIFIED + + * `OAUTH2` - OAUTH2 + + * `ACCESS_KEY_SECRET_KEY` - ACCESS_KEY_SECRET_KEY + + * `USERNAME_PASSWORD` - USERNAME_PASSWORD + + * `PERSONAL_ACCESS_TOKEN` - PERSONAL_ACCESS_TOKEN + + * `OAUTH1A` - OAUTH1A + + * `DATAVERSE_API_TOKEN` - DATAVERSE_API_TOKEN' + enum: + - UNSPECIFIED + - OAUTH2 + - ACCESS_KEY_SECRET_KEY + - USERNAME_PASSWORD + - PERSONAL_ACCESS_TOKEN + - OAUTH1A + - DATAVERSE_API_TOKEN + readOnly: true + type: string + display_name: + type: string + external_service_name: + readOnly: true + type: string + icon_url: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + supported_features: + items: + description: '* `FORKING_PARTIAL` - FORKING_PARTIAL + + * `PERMISSIONS_PARTIAL` - PERMISSIONS_PARTIAL' + enum: + - FORKING_PARTIAL + - PERMISSIONS_PARTIAL + type: string + readOnly: true + type: array + wb_key: + type: string + required: + - display_name + type: object + id: {} + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + addon_imp: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-imps + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Addon imp + type: object + required: + - addon_imp + type: object + type: + allOf: + - $ref: '#/components/schemas/ExternalCitationServiceTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + - id + type: object + ExternalCitationServiceResponse: + properties: + data: + $ref: '#/components/schemas/ExternalCitationService' + required: + - data + type: object + ExternalCitationServiceTypeEnum: + enum: + - external-citation-services + type: string + ExternalComputingService: + additionalProperties: false + properties: + attributes: + properties: + api_base_url_options: + items: + type: string + readOnly: true + type: array + auth_uri: + readOnly: true + type: string + configurable_api_root: + readOnly: true + type: string + credentials_format: + description: '* `UNSPECIFIED` - UNSPECIFIED + + * `OAUTH2` - OAUTH2 + + * `ACCESS_KEY_SECRET_KEY` - ACCESS_KEY_SECRET_KEY + + * `USERNAME_PASSWORD` - USERNAME_PASSWORD + + * `PERSONAL_ACCESS_TOKEN` - PERSONAL_ACCESS_TOKEN + + * `OAUTH1A` - OAUTH1A + + * `DATAVERSE_API_TOKEN` - DATAVERSE_API_TOKEN' + enum: + - UNSPECIFIED + - OAUTH2 + - ACCESS_KEY_SECRET_KEY + - USERNAME_PASSWORD + - PERSONAL_ACCESS_TOKEN + - OAUTH1A + - DATAVERSE_API_TOKEN + readOnly: true + type: string + display_name: + type: string + icon_url: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + supported_features: + items: + description: '* `ADD_UPDATE_FILES_PARTIAL` - ADD_UPDATE_FILES_PARTIAL + + * `FORKING_PARTIAL` - FORKING_PARTIAL + + * `LOGS_PARTIAL` - LOGS_PARTIAL + + * `PERMISSIONS_PARTIAL` - PERMISSIONS_PARTIAL + + * `REGISTERING_PARTIAL` - REGISTERING_PARTIAL' + enum: + - ADD_UPDATE_FILES_PARTIAL + - FORKING_PARTIAL + - LOGS_PARTIAL + - PERMISSIONS_PARTIAL + - REGISTERING_PARTIAL + type: string + readOnly: true + type: array + wb_key: + type: string + required: + - display_name + type: object + id: {} + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + addon_imp: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-imps + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Addon imp + type: object + required: + - addon_imp + type: object + type: + allOf: + - $ref: '#/components/schemas/ExternalComputingServiceTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + - id + type: object + ExternalComputingServiceResponse: + properties: + data: + $ref: '#/components/schemas/ExternalComputingService' + required: + - data + type: object + ExternalComputingServiceTypeEnum: + enum: + - external-computing-services + type: string + ExternalLinkService: + additionalProperties: false + properties: + attributes: + properties: + api_base_url_options: + items: + type: string + readOnly: true + type: array + auth_uri: + readOnly: true + type: string + configurable_api_root: + readOnly: true + type: string + credentials_format: + description: '* `UNSPECIFIED` - UNSPECIFIED + + * `OAUTH2` - OAUTH2 + + * `ACCESS_KEY_SECRET_KEY` - ACCESS_KEY_SECRET_KEY + + * `USERNAME_PASSWORD` - USERNAME_PASSWORD + + * `PERSONAL_ACCESS_TOKEN` - PERSONAL_ACCESS_TOKEN + + * `OAUTH1A` - OAUTH1A + + * `DATAVERSE_API_TOKEN` - DATAVERSE_API_TOKEN' + enum: + - UNSPECIFIED + - OAUTH2 + - ACCESS_KEY_SECRET_KEY + - USERNAME_PASSWORD + - PERSONAL_ACCESS_TOKEN + - OAUTH1A + - DATAVERSE_API_TOKEN + readOnly: true + type: string + display_name: + type: string + external_service_name: + readOnly: true + type: string + icon_url: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + supported_features: + items: + description: '* `ADD_UPDATE_FILES` - ADD_UPDATE_FILES + + * `DELETE_FILES` - DELETE_FILES + + * `FORKING` - FORKING + + * `FORKING_PARTIAL` - FORKING_PARTIAL + + * `LOGS` - LOGS + + * `LOGS_PARTIAL` - LOGS_PARTIAL + + * `PERMISSIONS` - PERMISSIONS + + * `REGISTERING` - REGISTERING + + * `FILE_VERSIONS` - FILE_VERSIONS' + enum: + - ADD_UPDATE_FILES + - DELETE_FILES + - FORKING + - FORKING_PARTIAL + - LOGS + - LOGS_PARTIAL + - PERMISSIONS + - REGISTERING + - FILE_VERSIONS + type: string + readOnly: true + type: array + supported_resource_types: + items: + description: '* `Audiovisual` - Audiovisual + + * `Award` - Award + + * `Book` - Book + + * `BookChapter` - BookChapter + + * `Collection` - Collection + + * `ComputationalNotebook` - ComputationalNotebook + + * `ConferencePaper` - ConferencePaper + + * `ConferenceProceeding` - ConferenceProceeding + + * `DataPaper` - DataPaper + + * `Dataset` - Dataset + + * `Dissertation` - Dissertation + + * `Event` - Event + + * `Image` - Image + + * `Instrument` - Instrument + + * `InteractiveResource` - InteractiveResource + + * `Journal` - Journal + + * `JournalArticle` - JournalArticle + + * `Model` - Model + + * `OutputManagementPlan` - OutputManagementPlan + + * `PeerReview` - PeerReview + + * `PhysicalObject` - PhysicalObject + + * `Preprint` - Preprint + + * `Project` - Project + + * `Report` - Report + + * `Service` - Service + + * `Software` - Software + + * `Sound` - Sound + + * `Standard` - Standard + + * `StudyRegistration` - StudyRegistration + + * `Text` - Text + + * `Workflow` - Workflow + + * `Other` - Other' + enum: + - Audiovisual + - Award + - Book + - BookChapter + - Collection + - ComputationalNotebook + - ConferencePaper + - ConferenceProceeding + - DataPaper + - Dataset + - Dissertation + - Event + - Image + - Instrument + - InteractiveResource + - Journal + - JournalArticle + - Model + - OutputManagementPlan + - PeerReview + - PhysicalObject + - Preprint + - Project + - Report + - Service + - Software + - Sound + - Standard + - StudyRegistration + - Text + - Workflow + - Other + type: string + readOnly: true + type: array + wb_key: + type: string + required: + - display_name + type: object + id: {} + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + addon_imp: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-imps + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Addon imp + type: object + required: + - addon_imp + type: object + type: + allOf: + - $ref: '#/components/schemas/ExternalLinkServiceTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + - id + type: object + ExternalLinkServiceResponse: + properties: + data: + $ref: '#/components/schemas/ExternalLinkService' + required: + - data + type: object + ExternalLinkServiceTypeEnum: + enum: + - external-link-services + type: string + ExternalStorageService: + additionalProperties: false + properties: + attributes: + properties: + api_base_url_options: + items: + type: string + readOnly: true + type: array + auth_uri: + readOnly: true + type: string + configurable_api_root: + readOnly: true + type: string + credentials_format: + description: '* `UNSPECIFIED` - UNSPECIFIED + + * `OAUTH2` - OAUTH2 + + * `ACCESS_KEY_SECRET_KEY` - ACCESS_KEY_SECRET_KEY + + * `USERNAME_PASSWORD` - USERNAME_PASSWORD + + * `PERSONAL_ACCESS_TOKEN` - PERSONAL_ACCESS_TOKEN + + * `OAUTH1A` - OAUTH1A + + * `DATAVERSE_API_TOKEN` - DATAVERSE_API_TOKEN' + enum: + - UNSPECIFIED + - OAUTH2 + - ACCESS_KEY_SECRET_KEY + - USERNAME_PASSWORD + - PERSONAL_ACCESS_TOKEN + - OAUTH1A + - DATAVERSE_API_TOKEN + readOnly: true + type: string + display_name: + type: string + external_service_name: + readOnly: true + type: string + icon_url: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + max_concurrent_downloads: + maximum: 2147483647 + minimum: -2147483648 + type: integer + max_upload_mb: + maximum: 2147483647 + minimum: -2147483648 + type: integer + supported_features: + items: + description: '* `ADD_UPDATE_FILES` - ADD_UPDATE_FILES + + * `ADD_UPDATE_FILES_PARTIAL` - ADD_UPDATE_FILES_PARTIAL + + * `DELETE_FILES` - DELETE_FILES + + * `DELETE_FILES_PARTIAL` - DELETE_FILES_PARTIAL + + * `FORKING` - FORKING + + * `LOGS` - LOGS + + * `PERMISSIONS` - PERMISSIONS + + * `REGISTERING` - REGISTERING + + * `FILE_VERSIONS` - FILE_VERSIONS + + * `COPY_INTO` - COPY_INTO + + * `DOWNLOAD_AS_ZIP` - DOWNLOAD_AS_ZIP' + enum: + - ADD_UPDATE_FILES + - ADD_UPDATE_FILES_PARTIAL + - DELETE_FILES + - DELETE_FILES_PARTIAL + - FORKING + - LOGS + - PERMISSIONS + - REGISTERING + - FILE_VERSIONS + - COPY_INTO + - DOWNLOAD_AS_ZIP + type: string + readOnly: true + type: array + wb_key: + type: string + required: + - max_concurrent_downloads + - max_upload_mb + - display_name + type: object + id: {} + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + addon_imp: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - addon-imps + title: Resource Type Name + type: string + required: + - id + - type + type: object + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Addon imp + type: object + required: + - addon_imp + type: object + type: + allOf: + - $ref: '#/components/schemas/ExternalStorageServiceTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + - id + type: object + ExternalStorageServiceResponse: + properties: + data: + $ref: '#/components/schemas/ExternalStorageService' + required: + - data + type: object + ExternalStorageServiceTypeEnum: + enum: + - external-storage-services + type: string + PaginatedExternalCitationServiceList: + properties: + data: + items: + $ref: '#/components/schemas/ExternalCitationService' + type: array + links: + properties: + first: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + last: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + next: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + prev: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + required: + - count + - next + - prev + type: object + meta: + properties: + pagination: + properties: + count: + example: 2 + type: integer + page: + example: 1 + type: integer + pages: + example: 1 + type: integer + required: + - count + - next + - prev + type: object + required: + - pagination + type: object + required: + - meta + - data + - links + type: object + PaginatedExternalComputingServiceList: + properties: + data: + items: + $ref: '#/components/schemas/ExternalComputingService' + type: array + links: + properties: + first: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + last: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + next: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + prev: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + required: + - count + - next + - prev + type: object + meta: + properties: + pagination: + properties: + count: + example: 2 + type: integer + page: + example: 1 + type: integer + pages: + example: 1 + type: integer + required: + - count + - next + - prev + type: object + required: + - pagination + type: object + required: + - meta + - data + - links + type: object + PaginatedExternalLinkServiceList: + properties: + data: + items: + $ref: '#/components/schemas/ExternalLinkService' + type: array + links: + properties: + first: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + last: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + next: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + prev: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + required: + - count + - next + - prev + type: object + meta: + properties: + pagination: + properties: + count: + example: 2 + type: integer + page: + example: 1 + type: integer + pages: + example: 1 + type: integer + required: + - count + - next + - prev + type: object + required: + - pagination + type: object + required: + - meta + - data + - links + type: object + PaginatedExternalStorageServiceList: + properties: + data: + items: + $ref: '#/components/schemas/ExternalStorageService' + type: array + links: + properties: + first: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + last: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + next: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + prev: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + required: + - count + - next + - prev + type: object + meta: + properties: + pagination: + properties: + count: + example: 2 + type: integer + page: + example: 1 + type: integer + pages: + example: 1 + type: integer + required: + - count + - next + - prev + type: object + required: + - pagination + type: object + required: + - meta + - data + - links + type: object + PaginatedResourceReferenceList: + properties: + data: + items: + $ref: '#/components/schemas/ResourceReference' + type: array + links: + properties: + first: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + last: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + next: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + prev: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + required: + - count + - next + - prev + type: object + meta: + properties: + pagination: + properties: + count: + example: 2 + type: integer + page: + example: 1 + type: integer + pages: + example: 1 + type: integer + required: + - count + - next + - prev + type: object + required: + - pagination + type: object + required: + - meta + - data + - links + type: object + PaginatedUserReferenceList: + properties: + data: + items: + $ref: '#/components/schemas/UserReference' + type: array + links: + properties: + first: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + last: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + next: + example: http://api.example.org/accounts/?page[number]=3 + format: uri + nullable: true + type: string + prev: + example: http://api.example.org/accounts/?page[number]=1 + format: uri + nullable: true + type: string + required: + - count + - next + - prev + type: object + meta: + properties: + pagination: + properties: + count: + example: 2 + type: integer + page: + example: 1 + type: integer + pages: + example: 1 + type: integer + required: + - count + - next + - prev + type: object + required: + - pagination + type: object + required: + - meta + - data + - links + type: object + PatchedAuthorizedCitationAccountRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + minLength: 1 + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + default_root_folder: + type: string + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + id: {} + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_citation_addons: + description: A related resource object from type configured-citation-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - configured-citation-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + required: + - data + title: Configured citation addons + type: object + external_citation_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-citation-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: External citation service + type: object + required: + - external_citation_service + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - authorized-citation-accounts + type: string + required: + - type + - id + type: object + required: + - data + type: object + PatchedAuthorizedComputingAccountRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + minLength: 1 + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + id: {} + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_computing_addons: + description: A related resource object from type configured-computing-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - configured-computing-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + required: + - data + title: Configured computing addons + type: object + external_computing_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-computing-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: External computing service + type: object + required: + - external_computing_service + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - authorized-computing-accounts + type: string + required: + - type + - id + type: object + required: + - data + type: object + PatchedAuthorizedLinkAccountRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + minLength: 1 + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + id: {} + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_link_addons: + description: A related resource object from type configured-link-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - configured-link-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + required: + - data + title: Configured link addons + type: object + external_link_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-link-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: External link service + type: object + required: + - external_link_service + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - authorized-link-accounts + type: string + required: + - type + - id + type: object + required: + - data + type: object + PatchedAuthorizedStorageAccountRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + api_base_url: + format: uri + nullable: true + type: string + auth_url: + minLength: 1 + readOnly: true + type: string + authorized_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + authorized_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + credentials: + writeOnly: true + credentials_available: + readOnly: true + type: boolean + default_root_folder: + type: string + display_name: + maxLength: 256 + nullable: true + type: string + id: + format: uuid + readOnly: true + type: string + initiate_oauth: + type: boolean + writeOnly: true + required: + - authorized_capabilities + type: object + id: {} + relationships: + properties: + account_owner: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - user-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Account owner + type: object + authorized_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized operations + type: object + configured_storage_addons: + description: A related resource object from type configured-storage-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - configured-storage-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + required: + - data + title: Configured storage addons + type: object + external_storage_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-storage-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: External storage service + type: object + required: + - external_storage_service + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - authorized-storage-accounts + type: string + required: + - type + - id + type: object + required: + - data + type: object + PatchedConfiguredCitationAddonRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + minLength: 1 + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + minLength: 1 + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + root_folder: + type: string + required: + - connected_capabilities + type: object + id: {} + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - authorized-citation-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_citation_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-citation-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: External citation service + type: object + required: + - base_account + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - configured-citation-addons + type: string + required: + - type + - id + type: object + required: + - data + type: object + PatchedConfiguredComputingAddonRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + minLength: 1 + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + minLength: 1 + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + required: + - connected_capabilities + type: object + id: {} + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - authorized-computing-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_computing_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-computing-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: External computing service + type: object + required: + - base_account + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - configured-computing-addons + type: string + required: + - type + - id + type: object + required: + - data + type: object + PatchedConfiguredLinkAddonRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + minLength: 1 + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + resource_type: + description: '* `Audiovisual` - Audiovisual + + * `Award` - Award + + * `Book` - Book + + * `BookChapter` - BookChapter + + * `Collection` - Collection + + * `ComputationalNotebook` - ComputationalNotebook + + * `ConferencePaper` - ConferencePaper + + * `ConferenceProceeding` - ConferenceProceeding + + * `DataPaper` - DataPaper + + * `Dataset` - Dataset + + * `Dissertation` - Dissertation + + * `Event` - Event + + * `Image` - Image + + * `Instrument` - Instrument + + * `InteractiveResource` - InteractiveResource + + * `Journal` - Journal + + * `JournalArticle` - JournalArticle + + * `Model` - Model + + * `OutputManagementPlan` - OutputManagementPlan + + * `PeerReview` - PeerReview + + * `PhysicalObject` - PhysicalObject + + * `Preprint` - Preprint + + * `Project` - Project + + * `Report` - Report + + * `Service` - Service + + * `Software` - Software + + * `Sound` - Sound + + * `Standard` - Standard + + * `StudyRegistration` - StudyRegistration + + * `Text` - Text + + * `Workflow` - Workflow + + * `Other` - Other' + enum: + - Audiovisual + - Award + - Book + - BookChapter + - Collection + - ComputationalNotebook + - ConferencePaper + - ConferenceProceeding + - DataPaper + - Dataset + - Dissertation + - Event + - Image + - Instrument + - InteractiveResource + - Journal + - JournalArticle + - Model + - OutputManagementPlan + - PeerReview + - PhysicalObject + - Preprint + - Project + - Report + - Service + - Software + - Sound + - Standard + - StudyRegistration + - Text + - Workflow + - Other + - null + nullable: true + type: string + target_id: + minLength: 1 + nullable: true + type: string + target_url: + format: uri + minLength: 1 + readOnly: true + type: string + required: + - connected_capabilities + type: object + id: {} + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - authorized-link-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_link_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-link-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: External link service + type: object + required: + - base_account + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - configured-link-addons + type: string + required: + - type + - id + type: object + required: + - data + type: object + PatchedConfiguredStorageAddonRequest: + properties: + data: + additionalProperties: false + properties: + attributes: + properties: + authorized_resource_uri: + minLength: 1 + type: string + writeOnly: true + connected_capabilities: + items: + description: '* `ACCESS` - ACCESS + + * `UPDATE` - UPDATE' + enum: + - ACCESS + - UPDATE + type: string + type: array + connected_operation_names: + items: + minLength: 1 + type: string + readOnly: true + type: array + current_user_is_owner: + readOnly: true + type: boolean + display_name: + maxLength: 256 + nullable: true + type: string + external_service_name: + readOnly: true + type: string + id: + format: uuid + readOnly: true + type: string + root_folder: + type: string + required: + - connected_capabilities + type: object + id: {} + relationships: + properties: + authorized_resource: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Authorized resource + type: object + base_account: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - authorized-storage-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + required: + - data + title: Base account + type: object + connected_operations: + description: The identifier of the related object. + properties: + data: + properties: + id: + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - addon-operations + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: Connected operations + type: object + external_storage_service: + description: The identifier of the related object. + properties: + data: + properties: + id: + format: uuid + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share + common attributes and relationships. + enum: + - external-storage-services + title: Resource Type Name + type: string + required: + - id + - type + type: object + readOnly: true + required: + - data + title: External storage service + type: object + required: + - base_account + type: object + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + enum: + - configured-storage-addons + type: string + required: + - type + - id + type: object + required: + - data + type: object + ResourceReference: + additionalProperties: false + properties: + attributes: + properties: + resource_uri: + format: uri + maxLength: 200 + type: string + required: + - resource_uri + type: object + id: + format: uuid + type: string + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + configured_citation_addons: + description: A related resource object from type configured-citation-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - configured-citation-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Configured citation addons + type: object + configured_computing_addons: + description: A related resource object from type configured-computing-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - configured-computing-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Configured computing addons + type: object + configured_link_addons: + description: A related resource object from type configured-link-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - configured-link-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Configured link addons + type: object + configured_storage_addons: + description: A related resource object from type configured-storage-addons + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - configured-storage-addons + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Configured storage addons + type: object + required: + - configured_storage_addons + - configured_link_addons + - configured_citation_addons + - configured_computing_addons + type: object + type: + allOf: + - $ref: '#/components/schemas/ResourceReferenceTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + - id + type: object + ResourceReferenceResponse: + properties: + data: + $ref: '#/components/schemas/ResourceReference' + required: + - data + type: object + ResourceReferenceTypeEnum: + enum: + - resource-references + type: string + UserReference: + additionalProperties: false + properties: + attributes: + properties: + user_uri: + format: uri + maxLength: 200 + type: string + required: + - user_uri + type: object + id: + format: uuid + type: string + links: + properties: + self: + example: http://api.example.org/accounts/123 + format: uri + nullable: false + type: string + type: object + relationships: + properties: + authorized_citation_accounts: + description: A related resource object from type authorized-citation-accounts + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - authorized-citation-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Authorized citation accounts + type: object + authorized_computing_accounts: + description: A related resource object from type authorized-computing-accounts + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - authorized-computing-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Authorized computing accounts + type: object + authorized_link_accounts: + description: A related resource object from type authorized-citation-accounts + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - authorized-citation-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Authorized link accounts + type: object + authorized_storage_accounts: + description: A related resource object from type authorized-storage-accounts + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - authorized-storage-accounts + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Authorized storage accounts + type: object + configured_resources: + description: A related resource object from type resource-references + properties: + data: + items: + properties: + id: + description: The identifier of the related object. + title: Resource Identifier + type: string + type: + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common + attributes and relationships. + enum: + - resource-references + title: Resource Type Name + type: string + required: + - id + - type + type: object + type: array + links: + properties: + related: + example: http://api.example.org/accounts/123 + format: uri + nullable: true + type: string + type: object + required: + - data + title: Configured resources + type: object + required: + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: object + type: + allOf: + - $ref: '#/components/schemas/UserReferenceTypeEnum' + description: The [type](https://jsonapi.org/format/#document-resource-object-identification) + member is used to describe resource objects that share common attributes + and relationships. + required: + - type + - id + type: object + UserReferenceResponse: + properties: + data: + $ref: '#/components/schemas/UserReference' + required: + - data + type: object + UserReferenceTypeEnum: + enum: + - user-references + type: string +info: + description: Addons service designed for use with OSF + title: GravyValet API + version: 1.0.0 +openapi: 3.0.3 +paths: + /v1/addon-imps/: + get: + description: viewset for read-only access to any `StaticDataclassModel` + operationId: addon_imps_list + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-imps] + schema: + items: + enum: + - url + - name + - docstring + - interface_docstring + - implemented_operations + type: string + type: array + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonImpResponse' + description: '' + security: + - {} + tags: + - addon-imps + /v1/addon-imps/{id}/: + get: + description: viewset for read-only access to any `StaticDataclassModel` + operationId: addon_imps_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-imps] + schema: + items: + enum: + - url + - name + - docstring + - interface_docstring + - implemented_operations + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonImpResponse' + description: '' + security: + - {} + tags: + - addon-imps + /v1/addon-imps/{id}/implemented_operations: + get: + description: Fetch all related AddonOperation to this AddonImp + operationId: addon_imps_retrieve_2_related_implemented_operations + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: + - addon-imps + /v1/addon-operation-invocations/: + post: + description: Perform some action using external service, for instance list files + on storage provider. In order to perform such action you need to include configured_addon + relationship + operationId: addon_operation_invocations_create + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationInvocationRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/AddonOperationInvocationRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/AddonOperationInvocationRequest' + required: true + responses: + '201': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationInvocationResponse' + description: '' + tags: + - addon-operation-invocations + /v1/addon-operation-invocations/{id}/: + get: + description: Get singular instance of addon operation invocation by it's pk. + May be useful to view action log + operationId: addon_operation_invocations_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operation-invocations] + schema: + items: + enum: + - url + - invocation_status + - operation_kwargs + - operation_result + - operation + - by_user + - thru_account + - thru_addon + - created + - modified + - operation_name + type: string + type: array + - description: A UUID string identifying this addon operation invocation. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - thru_account + - thru_addon + - operation + - by_user + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationInvocationResponse' + description: '' + tags: + - addon-operation-invocations + /v1/addon-operation-invocations/{id}/by_user: + get: + description: Fetch all related UserReference to this AddonOperationInvocation + operationId: addon_operation_invocations_retrieve_2_related_by_user + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[user-references] + schema: + items: + enum: + - url + - user_uri + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + - description: A UUID string identifying this User Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/UserReferenceResponse' + description: '' + tags: &id001 + - addon-operation-invocations + /v1/addon-operation-invocations/{id}/operation: + get: + description: Fetch all related AddonOperation to this AddonOperationInvocation + operationId: addon_operation_invocations_retrieve_2_related_operation + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: *id001 + /v1/addon-operation-invocations/{id}/thru_account: + get: + description: Fetch all related AuthorizedStorageAccount to this AddonOperationInvocation + operationId: addon_operation_invocations_retrieve_2_related_thru_account + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-storage-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_storage_addons + - credentials + - default_root_folder + - external_storage_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Storage Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_storage_service + - configured_storage_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedStorageAccountResponse' + description: '' + tags: *id001 + /v1/addon-operation-invocations/{id}/thru_addon: + get: + description: Fetch all related ConfiguredStorageAddon to this AddonOperationInvocation + operationId: addon_operation_invocations_retrieve_2_related_thru_addon + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-storage-addons] + schema: + items: + enum: + - id + - url + - display_name + - root_folder + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_storage_service + - current_user_is_owner + - external_service_name + type: string + type: array + - description: A UUID string identifying this Configured Storage Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_storage_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredStorageAddonResponse' + description: '' + tags: *id001 + /v1/addon-operations/: + get: + description: viewset for read-only access to any `StaticDataclassModel` + operationId: addon_operations_list + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: + - addon-operations + /v1/addon-operations/{id}/: + get: + description: viewset for read-only access to any `StaticDataclassModel` + operationId: addon_operations_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: + - addon-operations + /v1/addon-operations/{id}/implemented_by: + get: + description: Fetch all related AddonImp to this AddonOperation + operationId: addon_operations_retrieve_2_related_implemented_by + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-imps] + schema: + items: + enum: + - url + - name + - docstring + - interface_docstring + - implemented_operations + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonImpResponse' + description: '' + security: + - {} + tags: + - addon-operations + /v1/authorized-citation-accounts/: + post: + description: "Create new authorized citation account for given external citation\ + \ service.\n For OAuth services it's required to create account with `\"initiate_oauth\"\ + =true` in order to proceed with OAuth flow" + operationId: authorized_citation_accounts_create + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedCitationAccountRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/AuthorizedCitationAccountRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/AuthorizedCitationAccountRequest' + required: true + responses: + '201': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedCitationAccountResponse' + description: '' + tags: + - authorized-citation-accounts + /v1/authorized-citation-accounts/{id}/: + delete: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_citation_accounts_destroy + parameters: + - description: A UUID string identifying this Authorized Citation Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + '204': + description: No response body + tags: + - authorized-citation-accounts + get: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_citation_accounts_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-citation-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_citation_addons + - credentials + - default_root_folder + - external_citation_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Citation Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_citation_service + - configured_citation_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedCitationAccountResponse' + description: '' + tags: + - authorized-citation-accounts + patch: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_citation_accounts_partial_update + parameters: + - description: A UUID string identifying this Authorized Citation Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PatchedAuthorizedCitationAccountRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedAuthorizedCitationAccountRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedAuthorizedCitationAccountRequest' + required: true + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedCitationAccountResponse' + description: '' + tags: + - authorized-citation-accounts + /v1/authorized-citation-accounts/{id}/account_owner: + get: + description: Fetch all related UserReference to this AuthorizedCitationAccount + operationId: authorized_citation_accounts_retrieve_2_related_account_owner + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[user-references] + schema: + items: + enum: + - url + - user_uri + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + - description: A UUID string identifying this User Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/UserReferenceResponse' + description: '' + tags: &id002 + - authorized-citation-accounts + /v1/authorized-citation-accounts/{id}/authorized_operations: + get: + description: Fetch all related AddonOperation to this AuthorizedCitationAccount + operationId: authorized_citation_accounts_retrieve_2_related_authorized_operations + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: *id002 + /v1/authorized-citation-accounts/{id}/configured_citation_addons: + get: + description: Fetch all related ConfiguredCitationAddon to this AuthorizedCitationAccount + operationId: authorized_citation_accounts_retrieve_2_related_configured_citation_addons + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-citation-addons] + schema: + items: + enum: + - id + - url + - display_name + - root_folder + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_service_name + - external_citation_service + - current_user_is_owner + type: string + type: array + - description: A UUID string identifying this Configured Citation Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_citation_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredCitationAddonResponse' + description: '' + tags: *id002 + /v1/authorized-citation-accounts/{id}/external_citation_service: + get: + description: Fetch all related ExternalCitationService to this AuthorizedCitationAccount + operationId: authorized_citation_accounts_retrieve_2_related_external_citation_service + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-citation-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - configurable_api_root + - wb_key + - external_service_name + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Citation Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalCitationServiceResponse' + description: '' + security: + - {} + tags: *id002 + /v1/authorized-computing-accounts/: + post: + description: "Create new authorized computing account for given external computing\ + \ service.\n For OAuth services it's required to create account with `\"initiate_oauth\"\ + =true` in order to proceed with OAuth flow" + operationId: authorized_computing_accounts_create + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedComputingAccountRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/AuthorizedComputingAccountRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/AuthorizedComputingAccountRequest' + required: true + responses: + '201': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedComputingAccountResponse' + description: '' + tags: + - authorized-computing-accounts + /v1/authorized-computing-accounts/{id}/: + delete: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_computing_accounts_destroy + parameters: + - description: A UUID string identifying this Authorized Computing Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + '204': + description: No response body + tags: + - authorized-computing-accounts + get: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_computing_accounts_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-computing-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_computing_addons + - credentials + - external_computing_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Computing Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_computing_service + - configured_computing_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedComputingAccountResponse' + description: '' + tags: + - authorized-computing-accounts + patch: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_computing_accounts_partial_update + parameters: + - description: A UUID string identifying this Authorized Computing Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PatchedAuthorizedComputingAccountRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedAuthorizedComputingAccountRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedAuthorizedComputingAccountRequest' + required: true + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedComputingAccountResponse' + description: '' + tags: + - authorized-computing-accounts + /v1/authorized-computing-accounts/{id}/account_owner: + get: + description: Fetch all related UserReference to this AuthorizedComputingAccount + operationId: authorized_computing_accounts_retrieve_2_related_account_owner + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[user-references] + schema: + items: + enum: + - url + - user_uri + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + - description: A UUID string identifying this User Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/UserReferenceResponse' + description: '' + tags: &id003 + - authorized-computing-accounts + /v1/authorized-computing-accounts/{id}/authorized_operations: + get: + description: Fetch all related AddonOperation to this AuthorizedComputingAccount + operationId: authorized_computing_accounts_retrieve_2_related_authorized_operations + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: *id003 + /v1/authorized-computing-accounts/{id}/configured_computing_addons: + get: + description: Fetch all related ConfiguredComputingAddon to this AuthorizedComputingAccount + operationId: authorized_computing_accounts_retrieve_2_related_configured_computing_addons + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-computing-addons] + schema: + items: + enum: + - id + - url + - display_name + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_service_name + - external_computing_service + - current_user_is_owner + type: string + type: array + - description: A UUID string identifying this Configured Computing Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_computing_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredComputingAddonResponse' + description: '' + tags: *id003 + /v1/authorized-computing-accounts/{id}/external_computing_service: + get: + description: Fetch all related ExternalComputingService to this AuthorizedComputingAccount + operationId: authorized_computing_accounts_retrieve_2_related_external_computing_service + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-computing-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - configurable_api_root + - supported_features + - icon_url + - wb_key + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Computing Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalComputingServiceResponse' + description: '' + security: + - {} + tags: *id003 + /v1/authorized-link-accounts/: + post: + description: "Create new authorized link account for given external link service.\n\ + \ For OAuth services it's required to create account with `\"initiate_oauth\"\ + =true` in order to proceed with OAuth flow" + operationId: authorized_link_accounts_create + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedLinkAccountRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/AuthorizedLinkAccountRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/AuthorizedLinkAccountRequest' + required: true + responses: + '201': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedLinkAccountResponse' + description: '' + tags: + - authorized-link-accounts + /v1/authorized-link-accounts/{id}/: + delete: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_link_accounts_destroy + parameters: + - description: A UUID string identifying this Authorized Link Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + '204': + description: No response body + tags: + - authorized-link-accounts + get: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_link_accounts_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-link-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_link_addons + - credentials + - external_link_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Link Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_link_service + - configured_link_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedLinkAccountResponse' + description: '' + tags: + - authorized-link-accounts + patch: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_link_accounts_partial_update + parameters: + - description: A UUID string identifying this Authorized Link Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PatchedAuthorizedLinkAccountRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedAuthorizedLinkAccountRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedAuthorizedLinkAccountRequest' + required: true + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedLinkAccountResponse' + description: '' + tags: + - authorized-link-accounts + /v1/authorized-link-accounts/{id}/account_owner: + get: + description: Fetch all related UserReference to this AuthorizedLinkAccount + operationId: authorized_link_accounts_retrieve_2_related_account_owner + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[user-references] + schema: + items: + enum: + - url + - user_uri + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + - description: A UUID string identifying this User Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/UserReferenceResponse' + description: '' + tags: &id004 + - authorized-link-accounts + /v1/authorized-link-accounts/{id}/authorized_operations: + get: + description: Fetch all related AddonOperation to this AuthorizedLinkAccount + operationId: authorized_link_accounts_retrieve_2_related_authorized_operations + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: *id004 + /v1/authorized-link-accounts/{id}/configured_link_addons: + get: + description: Fetch all related ConfiguredLinkAddon to this AuthorizedLinkAccount + operationId: authorized_link_accounts_retrieve_2_related_configured_link_addons + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-link-addons] + schema: + items: + enum: + - id + - display_name + - target_url + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_link_service + - current_user_is_owner + - external_service_name + - resource_type + - target_id + type: string + type: array + - description: A UUID string identifying this Configured Link Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_link_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredLinkAddonResponse' + description: '' + tags: *id004 + /v1/authorized-link-accounts/{id}/external_link_service: + get: + description: Fetch all related ExternalLinkService to this AuthorizedLinkAccount + operationId: authorized_link_accounts_retrieve_2_related_external_link_service + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-link-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - wb_key + - external_service_name + - configurable_api_root + - supported_resource_types + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Link Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalLinkServiceResponse' + description: '' + security: + - {} + tags: *id004 + /v1/authorized-storage-accounts/: + post: + description: "Create new authorized storage account for given external storage\ + \ service.\n For OAuth services it's required to create account with `\"initiate_oauth\"\ + =true` in order to proceed with OAuth flow" + operationId: authorized_storage_accounts_create + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedStorageAccountRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/AuthorizedStorageAccountRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/AuthorizedStorageAccountRequest' + required: true + responses: + '201': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedStorageAccountResponse' + description: '' + tags: + - authorized-storage-accounts + /v1/authorized-storage-accounts/{id}/: + delete: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_storage_accounts_destroy + parameters: + - description: A UUID string identifying this Authorized Storage Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + '204': + description: No response body + tags: + - authorized-storage-accounts + get: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_storage_accounts_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-storage-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_storage_addons + - credentials + - default_root_folder + - external_storage_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Storage Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_storage_service + - configured_storage_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedStorageAccountResponse' + description: '' + tags: + - authorized-storage-accounts + patch: + description: viewset allowing create, retrieve, update, delete + operationId: authorized_storage_accounts_partial_update + parameters: + - description: A UUID string identifying this Authorized Storage Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PatchedAuthorizedStorageAccountRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedAuthorizedStorageAccountRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedAuthorizedStorageAccountRequest' + required: true + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedStorageAccountResponse' + description: '' + tags: + - authorized-storage-accounts + /v1/authorized-storage-accounts/{id}/account_owner: + get: + description: Fetch all related UserReference to this AuthorizedStorageAccount + operationId: authorized_storage_accounts_retrieve_2_related_account_owner + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[user-references] + schema: + items: + enum: + - url + - user_uri + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + - description: A UUID string identifying this User Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/UserReferenceResponse' + description: '' + tags: &id005 + - authorized-storage-accounts + /v1/authorized-storage-accounts/{id}/authorized_operations: + get: + description: Fetch all related AddonOperation to this AuthorizedStorageAccount + operationId: authorized_storage_accounts_retrieve_2_related_authorized_operations + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: *id005 + /v1/authorized-storage-accounts/{id}/external_storage_service: + get: + description: Fetch all related ExternalStorageService to this AuthorizedStorageAccount + operationId: authorized_storage_accounts_retrieve_2_related_external_storage_service + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-storage-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - max_concurrent_downloads + - max_upload_mb + - display_name + - url + - wb_key + - external_service_name + - configurable_api_root + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Storage Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalStorageServiceResponse' + description: '' + security: + - {} + tags: *id005 + /v1/configured-citation-addons/: + post: + description: "Create new configured citation addon for given authorized citation\ + \ account, linking it to desired project.\n To configure it properly, you\ + \ must specify `root_folder` on the provider's side.\n Note that everything\ + \ under this folder is going to be accessible to everyone who has access to\ + \ this project" + operationId: configured_citation_addons_create + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredCitationAddonRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/ConfiguredCitationAddonRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/ConfiguredCitationAddonRequest' + required: true + responses: + '201': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredCitationAddonResponse' + description: '' + tags: + - configured-citation-addons + /v1/configured-citation-addons/{id}/: + delete: + description: viewset allowing create, retrieve, update, delete + operationId: configured_citation_addons_destroy + parameters: + - description: A UUID string identifying this Configured Citation Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + '204': + description: No response body + tags: + - configured-citation-addons + get: + description: viewset allowing create, retrieve, update, delete + operationId: configured_citation_addons_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-citation-addons] + schema: + items: + enum: + - id + - url + - display_name + - root_folder + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_service_name + - external_citation_service + - current_user_is_owner + type: string + type: array + - description: A UUID string identifying this Configured Citation Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_citation_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredCitationAddonResponse' + description: '' + tags: + - configured-citation-addons + patch: + description: viewset allowing create, retrieve, update, delete + operationId: configured_citation_addons_partial_update + parameters: + - description: A UUID string identifying this Configured Citation Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PatchedConfiguredCitationAddonRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedConfiguredCitationAddonRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedConfiguredCitationAddonRequest' + required: true + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredCitationAddonResponse' + description: '' + tags: + - configured-citation-addons + /v1/configured-citation-addons/{id}/authorized_resource: + get: + description: Fetch all related ResourceReference to this ConfiguredCitationAddon + operationId: configured_citation_addons_retrieve_2_related_authorized_resource + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[resource-references] + schema: + items: + enum: + - url + - resource_uri + - configured_storage_addons + - configured_link_addons + - configured_citation_addons + - configured_computing_addons + type: string + type: array + - description: A UUID string identifying this Resource Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - configured_storage_addons + - configured_citation_addons + - configured_link_addons + - configured_computing_addons + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ResourceReferenceResponse' + description: '' + tags: &id006 + - configured-citation-addons + /v1/configured-citation-addons/{id}/base_account: + get: + description: Fetch all related AuthorizedCitationAccount to this ConfiguredCitationAddon + operationId: configured_citation_addons_retrieve_2_related_base_account + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-citation-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_citation_addons + - credentials + - default_root_folder + - external_citation_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Citation Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_citation_service + - configured_citation_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedCitationAccountResponse' + description: '' + tags: *id006 + /v1/configured-citation-addons/{id}/connected_operations: + get: + description: Fetch all related AddonOperation to this ConfiguredCitationAddon + operationId: configured_citation_addons_retrieve_2_related_connected_operations + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: *id006 + /v1/configured-citation-addons/{id}/external_citation_service: + get: + description: Fetch all related ExternalCitationService to this ConfiguredCitationAddon + operationId: configured_citation_addons_retrieve_2_related_external_citation_service + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-citation-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - configurable_api_root + - wb_key + - external_service_name + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Citation Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalCitationServiceResponse' + description: '' + security: + - {} + tags: *id006 + /v1/configured-computing-addons/: + post: + description: "Create new configured computing addon for given authorized computing\ + \ account, linking it to desired project.\n To configure it properly, you\ + \ must specify `root_folder` on the provider's side.\n Note that everything\ + \ under this folder is going to be accessible to everyone who has access to\ + \ this project" + operationId: configured_computing_addons_create + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredComputingAddonRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/ConfiguredComputingAddonRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/ConfiguredComputingAddonRequest' + required: true + responses: + '201': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredComputingAddonResponse' + description: '' + tags: + - configured-computing-addons + /v1/configured-computing-addons/{id}/: + delete: + description: viewset allowing create, retrieve, update, delete + operationId: configured_computing_addons_destroy + parameters: + - description: A UUID string identifying this Configured Computing Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + '204': + description: No response body + tags: + - configured-computing-addons + get: + description: viewset allowing create, retrieve, update, delete + operationId: configured_computing_addons_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-computing-addons] + schema: + items: + enum: + - id + - url + - display_name + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_service_name + - external_computing_service + - current_user_is_owner + type: string + type: array + - description: A UUID string identifying this Configured Computing Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_computing_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredComputingAddonResponse' + description: '' + tags: + - configured-computing-addons + patch: + description: viewset allowing create, retrieve, update, delete + operationId: configured_computing_addons_partial_update + parameters: + - description: A UUID string identifying this Configured Computing Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PatchedConfiguredComputingAddonRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedConfiguredComputingAddonRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedConfiguredComputingAddonRequest' + required: true + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredComputingAddonResponse' + description: '' + tags: + - configured-computing-addons + /v1/configured-computing-addons/{id}/authorized_resource: + get: + description: Fetch all related ResourceReference to this ConfiguredComputingAddon + operationId: configured_computing_addons_retrieve_2_related_authorized_resource + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[resource-references] + schema: + items: + enum: + - url + - resource_uri + - configured_storage_addons + - configured_link_addons + - configured_citation_addons + - configured_computing_addons + type: string + type: array + - description: A UUID string identifying this Resource Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - configured_storage_addons + - configured_citation_addons + - configured_link_addons + - configured_computing_addons + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ResourceReferenceResponse' + description: '' + tags: &id007 + - configured-computing-addons + /v1/configured-computing-addons/{id}/base_account: + get: + description: Fetch all related AuthorizedComputingAccount to this ConfiguredComputingAddon + operationId: configured_computing_addons_retrieve_2_related_base_account + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-computing-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_computing_addons + - credentials + - external_computing_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Computing Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_computing_service + - configured_computing_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedComputingAccountResponse' + description: '' + tags: *id007 + /v1/configured-computing-addons/{id}/connected_operations: + get: + description: Fetch all related AddonOperation to this ConfiguredComputingAddon + operationId: configured_computing_addons_retrieve_2_related_connected_operations + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: *id007 + /v1/configured-computing-addons/{id}/external_computing_service: + get: + description: Fetch all related ExternalComputingService to this ConfiguredComputingAddon + operationId: configured_computing_addons_retrieve_2_related_external_computing_service + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-computing-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - configurable_api_root + - supported_features + - icon_url + - wb_key + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Computing Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalComputingServiceResponse' + description: '' + security: + - {} + tags: *id007 + /v1/configured-link-addons/: + post: + description: "Create new configured link addon for given authorized link account,\ + \ linking it to desired project.\n To configure it properly, you must specify\ + \ `root_folder` on the provider's side.\n Note that everything under this\ + \ folder is going to be accessible to everyone who has access to this project" + operationId: configured_link_addons_create + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredLinkAddonRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/ConfiguredLinkAddonRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/ConfiguredLinkAddonRequest' + required: true + responses: + '201': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredLinkAddonResponse' + description: '' + tags: + - configured-link-addons + /v1/configured-link-addons/{id}/: + delete: + description: viewset allowing create, retrieve, update, delete + operationId: configured_link_addons_destroy + parameters: + - description: A UUID string identifying this Configured Link Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + '204': + description: No response body + tags: + - configured-link-addons + get: + description: viewset allowing create, retrieve, update, delete + operationId: configured_link_addons_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-link-addons] + schema: + items: + enum: + - id + - display_name + - target_url + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_link_service + - current_user_is_owner + - external_service_name + - resource_type + - target_id + type: string + type: array + - description: A UUID string identifying this Configured Link Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_link_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredLinkAddonResponse' + description: '' + tags: + - configured-link-addons + patch: + description: viewset allowing create, retrieve, update, delete + operationId: configured_link_addons_partial_update + parameters: + - description: A UUID string identifying this Configured Link Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PatchedConfiguredLinkAddonRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedConfiguredLinkAddonRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedConfiguredLinkAddonRequest' + required: true + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredLinkAddonResponse' + description: '' + tags: + - configured-link-addons + /v1/configured-link-addons/{id}/authorized_resource: + get: + description: Fetch all related ResourceReference to this ConfiguredLinkAddon + operationId: configured_link_addons_retrieve_2_related_authorized_resource + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[resource-references] + schema: + items: + enum: + - url + - resource_uri + - configured_storage_addons + - configured_link_addons + - configured_citation_addons + - configured_computing_addons + type: string + type: array + - description: A UUID string identifying this Resource Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - configured_storage_addons + - configured_citation_addons + - configured_link_addons + - configured_computing_addons + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ResourceReferenceResponse' + description: '' + tags: &id008 + - configured-link-addons + /v1/configured-link-addons/{id}/base_account: + get: + description: Fetch all related AuthorizedLinkAccount to this ConfiguredLinkAddon + operationId: configured_link_addons_retrieve_2_related_base_account + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-link-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_link_addons + - credentials + - external_link_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Link Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_link_service + - configured_link_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedLinkAccountResponse' + description: '' + tags: *id008 + /v1/configured-link-addons/{id}/connected_operations: + get: + description: Fetch all related AddonOperation to this ConfiguredLinkAddon + operationId: configured_link_addons_retrieve_2_related_connected_operations + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: *id008 + /v1/configured-link-addons/{id}/external_link_service: + get: + description: Fetch all related ExternalLinkService to this ConfiguredLinkAddon + operationId: configured_link_addons_retrieve_2_related_external_link_service + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-link-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - wb_key + - external_service_name + - configurable_api_root + - supported_resource_types + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Link Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalLinkServiceResponse' + description: '' + security: + - {} + tags: *id008 + /v1/configured-link-addons/{id}/verified-links/: + get: + description: viewset allowing create, retrieve, update, delete + operationId: configured_link_addons_verified_links_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-link-addons] + schema: + items: + enum: + - id + - display_name + - target_url + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_link_service + - current_user_is_owner + - external_service_name + - resource_type + - target_id + type: string + type: array + - description: A UUID string identifying this Configured Link Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_link_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredLinkAddonResponse' + description: '' + tags: + - configured-link-addons + /v1/configured-storage-addons/: + post: + description: "Create new configured storage addon for given authorized storage\ + \ account, linking it to desired project.\n To configure it properly, you\ + \ must specify `root_folder` on the provider's side.\n Note that everything\ + \ under this folder is going to be accessible to everyone who has access to\ + \ this project" + operationId: configured_storage_addons_create + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredStorageAddonRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/ConfiguredStorageAddonRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/ConfiguredStorageAddonRequest' + required: true + responses: + '201': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredStorageAddonResponse' + description: '' + tags: + - configured-storage-addons + /v1/configured-storage-addons/{id}/: + delete: + description: viewset allowing create, retrieve, update, delete + operationId: configured_storage_addons_destroy + parameters: + - description: A UUID string identifying this Configured Storage Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + '204': + description: No response body + tags: + - configured-storage-addons + get: + description: viewset allowing create, retrieve, update, delete + operationId: configured_storage_addons_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-storage-addons] + schema: + items: + enum: + - id + - url + - display_name + - root_folder + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_storage_service + - current_user_is_owner + - external_service_name + type: string + type: array + - description: A UUID string identifying this Configured Storage Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_storage_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredStorageAddonResponse' + description: '' + tags: + - configured-storage-addons + patch: + description: viewset allowing create, retrieve, update, delete + operationId: configured_storage_addons_partial_update + parameters: + - description: A UUID string identifying this Configured Storage Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PatchedConfiguredStorageAddonRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedConfiguredStorageAddonRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedConfiguredStorageAddonRequest' + required: true + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredStorageAddonResponse' + description: '' + tags: + - configured-storage-addons + /v1/configured-storage-addons/{id}/authorized_resource: + get: + description: Fetch all related ResourceReference to this ConfiguredStorageAddon + operationId: configured_storage_addons_retrieve_2_related_authorized_resource + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[resource-references] + schema: + items: + enum: + - url + - resource_uri + - configured_storage_addons + - configured_link_addons + - configured_citation_addons + - configured_computing_addons + type: string + type: array + - description: A UUID string identifying this Resource Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - configured_storage_addons + - configured_citation_addons + - configured_link_addons + - configured_computing_addons + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ResourceReferenceResponse' + description: '' + tags: &id009 + - configured-storage-addons + /v1/configured-storage-addons/{id}/base_account: + get: + description: Fetch all related AuthorizedStorageAccount to this ConfiguredStorageAddon + operationId: configured_storage_addons_retrieve_2_related_base_account + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-storage-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_storage_addons + - credentials + - default_root_folder + - external_storage_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Storage Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_storage_service + - configured_storage_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedStorageAccountResponse' + description: '' + tags: *id009 + /v1/configured-storage-addons/{id}/connected_operations: + get: + description: Fetch all related AddonOperation to this ConfiguredStorageAddon + operationId: configured_storage_addons_retrieve_2_related_connected_operations + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-operations] + schema: + items: + enum: + - url + - name + - docstring + - capability + - kwargs_jsonschema + - result_jsonschema + - implemented_by + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_by + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonOperationResponse' + description: '' + security: + - {} + tags: *id009 + /v1/configured-storage-addons/{id}/external_storage_service: + get: + description: Fetch all related ExternalStorageService to this ConfiguredStorageAddon + operationId: configured_storage_addons_retrieve_2_related_external_storage_service + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-storage-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - max_concurrent_downloads + - max_upload_mb + - display_name + - url + - wb_key + - external_service_name + - configurable_api_root + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Storage Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalStorageServiceResponse' + description: '' + security: + - {} + tags: *id009 + /v1/external-citation-services/: + get: + description: Get the list of all available external citation services + operationId: external_citation_services_list + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-citation-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - configurable_api_root + - wb_key + - external_service_name + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + - description: A page number within the paginated result set. + in: query + name: page[number] + required: false + schema: + type: integer + - description: Number of results to return per page. + in: query + name: page[size] + required: false + schema: + type: integer + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PaginatedExternalCitationServiceList' + description: '' + security: + - {} + tags: + - external-citation-services + /v1/external-citation-services/{id}/: + get: + description: "This mixin provides a helper attributes to select or prefetch\ + \ related models\nbased on the include specified in the URL.\n\n__all__ can\ + \ be used to specify a prefetch which should be done regardless of the include\n\ + \n\n.. code:: python\n\n # When MyViewSet is called with ?include=author\ + \ it will prefetch author and authorbio\n class MyViewSet(viewsets.ModelViewSet):\n\ + \ queryset = Book.objects.all()\n prefetch_for_includes = {\n\ + \ '__all__': [],\n 'category.section': ['category']\n\ + \ }\n select_for_includes = {\n '__all__': [],\n\ + \ 'author': ['author', 'author__authorbio'],\n }" + operationId: external_citation_services_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-citation-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - configurable_api_root + - wb_key + - external_service_name + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Citation Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalCitationServiceResponse' + description: '' + security: + - {} + tags: + - external-citation-services + /v1/external-citation-services/{id}/addon_imp: + get: + description: Fetch all related AddonImp to this ExternalCitationService + operationId: external_citation_services_retrieve_2_related_addon_imp + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-imps] + schema: + items: + enum: + - url + - name + - docstring + - interface_docstring + - implemented_operations + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonImpResponse' + description: '' + security: + - {} + tags: + - external-citation-services + /v1/external-computing-services/: + get: + description: Get the list of all available external computing services + operationId: external_computing_services_list + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-computing-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - configurable_api_root + - supported_features + - icon_url + - wb_key + - api_base_url_options + type: string + type: array + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + - description: A page number within the paginated result set. + in: query + name: page[number] + required: false + schema: + type: integer + - description: Number of results to return per page. + in: query + name: page[size] + required: false + schema: + type: integer + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PaginatedExternalComputingServiceList' + description: '' + security: + - {} + tags: + - external-computing-services + /v1/external-computing-services/{id}/: + get: + description: "This mixin provides a helper attributes to select or prefetch\ + \ related models\nbased on the include specified in the URL.\n\n__all__ can\ + \ be used to specify a prefetch which should be done regardless of the include\n\ + \n\n.. code:: python\n\n # When MyViewSet is called with ?include=author\ + \ it will prefetch author and authorbio\n class MyViewSet(viewsets.ModelViewSet):\n\ + \ queryset = Book.objects.all()\n prefetch_for_includes = {\n\ + \ '__all__': [],\n 'category.section': ['category']\n\ + \ }\n select_for_includes = {\n '__all__': [],\n\ + \ 'author': ['author', 'author__authorbio'],\n }" + operationId: external_computing_services_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-computing-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - configurable_api_root + - supported_features + - icon_url + - wb_key + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Computing Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalComputingServiceResponse' + description: '' + security: + - {} + tags: + - external-computing-services + /v1/external-computing-services/{id}/addon_imp: + get: + description: Fetch all related AddonImp to this ExternalComputingService + operationId: external_computing_services_retrieve_2_related_addon_imp + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-imps] + schema: + items: + enum: + - url + - name + - docstring + - interface_docstring + - implemented_operations + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonImpResponse' + description: '' + security: + - {} + tags: + - external-computing-services + /v1/external-link-services/: + get: + description: Get the list of all available external link services + operationId: external_link_services_list + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-link-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - wb_key + - external_service_name + - configurable_api_root + - supported_resource_types + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + - description: A page number within the paginated result set. + in: query + name: page[number] + required: false + schema: + type: integer + - description: Number of results to return per page. + in: query + name: page[size] + required: false + schema: + type: integer + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PaginatedExternalLinkServiceList' + description: '' + security: + - {} + tags: + - external-link-services + /v1/external-link-services/{id}/: + get: + description: "This mixin provides a helper attributes to select or prefetch\ + \ related models\nbased on the include specified in the URL.\n\n__all__ can\ + \ be used to specify a prefetch which should be done regardless of the include\n\ + \n\n.. code:: python\n\n # When MyViewSet is called with ?include=author\ + \ it will prefetch author and authorbio\n class MyViewSet(viewsets.ModelViewSet):\n\ + \ queryset = Book.objects.all()\n prefetch_for_includes = {\n\ + \ '__all__': [],\n 'category.section': ['category']\n\ + \ }\n select_for_includes = {\n '__all__': [],\n\ + \ 'author': ['author', 'author__authorbio'],\n }" + operationId: external_link_services_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-link-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - display_name + - url + - wb_key + - external_service_name + - configurable_api_root + - supported_resource_types + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Link Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalLinkServiceResponse' + description: '' + security: + - {} + tags: + - external-link-services + /v1/external-link-services/{id}/addon_imp: + get: + description: Fetch all related AddonImp to this ExternalLinkService + operationId: external_link_services_retrieve_2_related_addon_imp + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-imps] + schema: + items: + enum: + - url + - name + - docstring + - interface_docstring + - implemented_operations + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonImpResponse' + description: '' + security: + - {} + tags: + - external-link-services + /v1/external-storage-services/: + get: + description: Get the list of all available external storage services + operationId: external_storage_services_list + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-storage-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - max_concurrent_downloads + - max_upload_mb + - display_name + - url + - wb_key + - external_service_name + - configurable_api_root + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + - description: A page number within the paginated result set. + in: query + name: page[number] + required: false + schema: + type: integer + - description: Number of results to return per page. + in: query + name: page[size] + required: false + schema: + type: integer + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PaginatedExternalStorageServiceList' + description: '' + security: + - {} + tags: + - external-storage-services + /v1/external-storage-services/{id}/: + get: + description: "This mixin provides a helper attributes to select or prefetch\ + \ related models\nbased on the include specified in the URL.\n\n__all__ can\ + \ be used to specify a prefetch which should be done regardless of the include\n\ + \n\n.. code:: python\n\n # When MyViewSet is called with ?include=author\ + \ it will prefetch author and authorbio\n class MyViewSet(viewsets.ModelViewSet):\n\ + \ queryset = Book.objects.all()\n prefetch_for_includes = {\n\ + \ '__all__': [],\n 'category.section': ['category']\n\ + \ }\n select_for_includes = {\n '__all__': [],\n\ + \ 'author': ['author', 'author__authorbio'],\n }" + operationId: external_storage_services_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[external-storage-services] + schema: + items: + enum: + - id + - addon_imp + - auth_uri + - credentials_format + - max_concurrent_downloads + - max_upload_mb + - display_name + - url + - wb_key + - external_service_name + - configurable_api_root + - supported_features + - icon_url + - api_base_url_options + type: string + type: array + - description: A UUID string identifying this External Storage Service. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - addon_imp + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ExternalStorageServiceResponse' + description: '' + security: + - {} + tags: + - external-storage-services + /v1/external-storage-services/{id}/addon_imp: + get: + description: Fetch all related AddonImp to this ExternalStorageService + operationId: external_storage_services_retrieve_2_related_addon_imp + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[addon-imps] + schema: + items: + enum: + - url + - name + - docstring + - interface_docstring + - implemented_operations + type: string + type: array + - in: path + name: id + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - implemented_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AddonImpResponse' + description: '' + security: + - {} + tags: + - external-storage-services + /v1/resource-references/: + get: + description: Get resource reference by resource_uri. Even through this is a + list method, this endpoint returns only one entity + operationId: resource_references_list + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[resource-references] + schema: + items: + enum: + - url + - resource_uri + - configured_storage_addons + - configured_link_addons + - configured_citation_addons + - configured_computing_addons + type: string + type: array + - description: Filter by resource_uri. This filter must be uniquely identifying. + in: query + name: filter[resource_uri] + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - configured_storage_addons + - configured_citation_addons + - configured_link_addons + - configured_computing_addons + type: string + type: array + - description: A page number within the paginated result set. + in: query + name: page[number] + required: false + schema: + type: integer + - description: Number of results to return per page. + in: query + name: page[size] + required: false + schema: + type: integer + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PaginatedResourceReferenceList' + description: '' + tags: + - resource-references + /v1/resource-references/{id}/: + get: + description: Get resource reference by it's pk + operationId: resource_references_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[resource-references] + schema: + items: + enum: + - url + - resource_uri + - configured_storage_addons + - configured_link_addons + - configured_citation_addons + - configured_computing_addons + type: string + type: array + - description: A UUID string identifying this Resource Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - configured_storage_addons + - configured_citation_addons + - configured_link_addons + - configured_computing_addons + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ResourceReferenceResponse' + description: '' + tags: + - resource-references + /v1/resource-references/{id}/configured_citation_addons: + get: + description: Fetch all related ConfiguredCitationAddon to this ResourceReference + operationId: resource_references_retrieve_2_related_configured_citation_addons + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-citation-addons] + schema: + items: + enum: + - id + - url + - display_name + - root_folder + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_service_name + - external_citation_service + - current_user_is_owner + type: string + type: array + - description: A UUID string identifying this Configured Citation Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_citation_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredCitationAddonResponse' + description: '' + tags: &id010 + - resource-references + /v1/resource-references/{id}/configured_computing_addons: + get: + description: Fetch all related ConfiguredComputingAddon to this ResourceReference + operationId: resource_references_retrieve_2_related_configured_computing_addons + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-computing-addons] + schema: + items: + enum: + - id + - url + - display_name + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_service_name + - external_computing_service + - current_user_is_owner + type: string + type: array + - description: A UUID string identifying this Configured Computing Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_computing_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredComputingAddonResponse' + description: '' + tags: *id010 + /v1/resource-references/{id}/configured_link_addons: + get: + description: Fetch all related ConfiguredLinkAddon to this ResourceReference + operationId: resource_references_retrieve_2_related_configured_link_addons + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-link-addons] + schema: + items: + enum: + - id + - display_name + - target_url + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_link_service + - current_user_is_owner + - external_service_name + - resource_type + - target_id + type: string + type: array + - description: A UUID string identifying this Configured Link Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_link_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredLinkAddonResponse' + description: '' + tags: *id010 + /v1/resource-references/{id}/configured_storage_addons: + get: + description: Fetch all related ConfiguredStorageAddon to this ResourceReference + operationId: resource_references_retrieve_2_related_configured_storage_addons + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[configured-storage-addons] + schema: + items: + enum: + - id + - url + - display_name + - root_folder + - base_account + - authorized_resource + - authorized_resource_uri + - connected_capabilities + - connected_operations + - connected_operation_names + - external_storage_service + - current_user_is_owner + - external_service_name + type: string + type: array + - description: A UUID string identifying this Configured Storage Addon. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - base_account + - external_storage_service + - authorized_resource + - connected_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ConfiguredStorageAddonResponse' + description: '' + tags: *id010 + /v1/user-references/: + get: + description: Get user reference by user_uri. Even through this is a list method, + this endpoint returns only one entity + operationId: user_references_list + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[user-references] + schema: + items: + enum: + - url + - user_uri + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + - description: Filter by user_uri. This filter must be uniquely identifying. + in: query + name: filter[user_uri] + required: true + schema: + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + - description: A page number within the paginated result set. + in: query + name: page[number] + required: false + schema: + type: integer + - description: Number of results to return per page. + in: query + name: page[size] + required: false + schema: + type: integer + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PaginatedUserReferenceList' + description: '' + tags: + - user-references + /v1/user-references/{id}/: + get: + description: Get user reference by it's pk + operationId: user_references_retrieve + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[user-references] + schema: + items: + enum: + - url + - user_uri + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + - description: A UUID string identifying this User Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - authorized_storage_accounts + - authorized_citation_accounts + - authorized_computing_accounts + - authorized_link_accounts + - configured_resources + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/UserReferenceResponse' + description: '' + tags: + - user-references + /v1/user-references/{id}/authorized_citation_accounts: + get: + description: Fetch all related AuthorizedCitationAccount to this UserReference + operationId: user_references_retrieve_2_related_authorized_citation_accounts + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-citation-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_citation_addons + - credentials + - default_root_folder + - external_citation_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Citation Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_citation_service + - configured_citation_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedCitationAccountResponse' + description: '' + tags: &id011 + - user-references + /v1/user-references/{id}/authorized_computing_accounts: + get: + description: Fetch all related AuthorizedComputingAccount to this UserReference + operationId: user_references_retrieve_2_related_authorized_computing_accounts + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-computing-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_computing_addons + - credentials + - external_computing_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Computing Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_computing_service + - configured_computing_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedComputingAccountResponse' + description: '' + tags: *id011 + /v1/user-references/{id}/authorized_link_accounts: + get: + description: Fetch all related AuthorizedCitationAccount to this UserReference + operationId: user_references_retrieve_2_related_authorized_link_accounts + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-citation-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_citation_addons + - credentials + - default_root_folder + - external_citation_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Citation Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_citation_service + - configured_citation_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedCitationAccountResponse' + description: '' + tags: *id011 + /v1/user-references/{id}/authorized_storage_accounts: + get: + description: Fetch all related AuthorizedStorageAccount to this UserReference + operationId: user_references_retrieve_2_related_authorized_storage_accounts + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[authorized-storage-accounts] + schema: + items: + enum: + - id + - url + - display_name + - account_owner + - api_base_url + - auth_url + - authorized_capabilities + - authorized_operations + - authorized_operation_names + - configured_storage_addons + - credentials + - default_root_folder + - external_storage_service + - initiate_oauth + - credentials_available + type: string + type: array + - description: A UUID string identifying this Authorized Storage Account. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - account_owner + - external_storage_service + - configured_storage_addons + - authorized_operations + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/AuthorizedStorageAccountResponse' + description: '' + tags: *id011 + /v1/user-references/{id}/configured_resources: + get: + description: Fetch all related ResourceReference to this UserReference + operationId: user_references_retrieve_2_related_configured_resources + parameters: + - description: endpoint return only specific fields in the response on a per-type + basis by including a fields[TYPE] query parameter. + explode: false + in: query + name: fields[resource-references] + schema: + items: + enum: + - url + - resource_uri + - configured_storage_addons + - configured_link_addons + - configured_citation_addons + - configured_computing_addons + type: string + type: array + - description: A UUID string identifying this Resource Reference. + in: path + name: id + required: true + schema: + format: uuid + type: string + - description: include query parameter to allow the client to customize which + related resources should be returned. + explode: false + in: query + name: include + schema: + items: + enum: + - configured_storage_addons + - configured_citation_addons + - configured_link_addons + - configured_computing_addons + type: string + type: array + responses: + '200': + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ResourceReferenceResponse' + description: '' + tags: *id011 From a9bc59b4cd99c64d34f6ee7ee8ac77671b74e7e3 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 30 Jun 2025 20:23:13 +0300 Subject: [PATCH 090/100] removed openapi.yml --- .../commands/fill_external_services.py | 1 - openapi.yml | 11403 ---------------- 2 files changed, 11404 deletions(-) delete mode 100644 openapi.yml diff --git a/addon_service/management/commands/fill_external_services.py b/addon_service/management/commands/fill_external_services.py index dd074a43..fb572e0c 100644 --- a/addon_service/management/commands/fill_external_services.py +++ b/addon_service/management/commands/fill_external_services.py @@ -60,7 +60,6 @@ def load_csv_data(self, path: Path) -> dict[str, dict[str, Any]]: item = csv.reader(services_file) field_names = next(item) data_list = [dict(zip(field_names, service)) for service in item] - print(data_list) return { item.pop("id"): self.fix_values(item) for item in data_list diff --git a/openapi.yml b/openapi.yml deleted file mode 100644 index 075b60ce..00000000 --- a/openapi.yml +++ /dev/null @@ -1,11403 +0,0 @@ -components: - schemas: - AddonImp: - additionalProperties: false - properties: - attributes: - properties: - docstring: - readOnly: true - type: string - interface_docstring: - readOnly: true - type: string - name: - readOnly: true - type: string - type: object - id: {} - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - implemented_operations: - description: A related resource object from type addon-operations - properties: - data: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Implemented operations - type: object - type: object - type: - allOf: - - $ref: '#/components/schemas/AddonImpTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - - id - type: object - AddonImpResponse: - properties: - data: - items: - $ref: '#/components/schemas/AddonImp' - type: array - required: - - data - type: object - AddonImpTypeEnum: - enum: - - addon-imps - type: string - AddonOperation: - additionalProperties: false - properties: - attributes: - properties: - capability: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - readOnly: true - type: string - docstring: - readOnly: true - type: string - kwargs_jsonschema: - readOnly: true - name: - readOnly: true - type: string - result_jsonschema: - readOnly: true - type: object - id: {} - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - implemented_by: - description: A related resource object from type addon-imps - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-imps - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Implemented by - type: object - type: object - type: - allOf: - - $ref: '#/components/schemas/AddonOperationTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - - id - type: object - AddonOperationInvocation: - additionalProperties: false - properties: - attributes: - properties: - created: - format: date-time - readOnly: true - type: string - invocation_status: - description: '* `STARTING` - STARTING - - * `GOING` - GOING - - * `SUCCESS` - SUCCESS - - * `ERROR` - ERROR' - enum: - - STARTING - - GOING - - SUCCESS - - ERROR - readOnly: true - type: string - modified: - format: date-time - readOnly: true - type: string - operation_kwargs: {} - operation_name: - type: string - operation_result: - readOnly: true - required: - - operation_kwargs - - operation_name - type: object - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - by_user: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: By user - type: object - operation: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Operation - type: object - thru_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - AuthorizedAccount - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Thru account - type: object - thru_addon: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - ConfiguredAddon - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Thru addon - type: object - type: object - type: - allOf: - - $ref: '#/components/schemas/AddonOperationInvocationTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - type: object - AddonOperationInvocationRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - created: - format: date-time - readOnly: true - type: string - invocation_status: - description: '* `STARTING` - STARTING - - * `GOING` - GOING - - * `SUCCESS` - SUCCESS - - * `ERROR` - ERROR' - enum: - - STARTING - - GOING - - SUCCESS - - ERROR - readOnly: true - type: string - modified: - format: date-time - readOnly: true - type: string - operation_kwargs: {} - operation_name: - minLength: 1 - type: string - operation_result: - readOnly: true - required: - - operation_kwargs - - operation_name - type: object - relationships: - properties: - by_user: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: By user - type: object - operation: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Operation - type: object - thru_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - AuthorizedAccount - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Thru account - type: object - thru_addon: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - ConfiguredAddon - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Thru addon - type: object - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - addon-operation-invocations - type: string - required: - - type - type: object - required: - - data - type: object - AddonOperationInvocationResponse: - properties: - data: - $ref: '#/components/schemas/AddonOperationInvocation' - required: - - data - type: object - AddonOperationInvocationTypeEnum: - enum: - - addon-operation-invocations - type: string - AddonOperationResponse: - properties: - data: - items: - $ref: '#/components/schemas/AddonOperation' - type: array - required: - - data - type: object - AddonOperationTypeEnum: - enum: - - addon-operations - type: string - AuthorizedCitationAccount: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - default_root_folder: - type: string - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_citation_addons: - description: A related resource object from type configured-citation-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - configured-citation-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Configured citation addons - type: object - external_citation_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - external-citation-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: External citation service - type: object - required: - - external_citation_service - type: object - type: - allOf: - - $ref: '#/components/schemas/AuthorizedCitationAccountTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - type: object - AuthorizedCitationAccountRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - minLength: 1 - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - default_root_folder: - type: string - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_citation_addons: - description: A related resource object from type configured-citation-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - configured-citation-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - required: - - data - title: Configured citation addons - type: object - external_citation_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-citation-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: External citation service - type: object - required: - - external_citation_service - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - authorized-citation-accounts - type: string - required: - - type - type: object - required: - - data - type: object - AuthorizedCitationAccountResponse: - properties: - data: - $ref: '#/components/schemas/AuthorizedCitationAccount' - required: - - data - type: object - AuthorizedCitationAccountTypeEnum: - enum: - - authorized-citation-accounts - type: string - AuthorizedComputingAccount: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_computing_addons: - description: A related resource object from type configured-computing-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - configured-computing-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Configured computing addons - type: object - external_computing_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - external-computing-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: External computing service - type: object - required: - - external_computing_service - type: object - type: - allOf: - - $ref: '#/components/schemas/AuthorizedComputingAccountTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - type: object - AuthorizedComputingAccountRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - minLength: 1 - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_computing_addons: - description: A related resource object from type configured-computing-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - configured-computing-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - required: - - data - title: Configured computing addons - type: object - external_computing_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-computing-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: External computing service - type: object - required: - - external_computing_service - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - authorized-computing-accounts - type: string - required: - - type - type: object - required: - - data - type: object - AuthorizedComputingAccountResponse: - properties: - data: - $ref: '#/components/schemas/AuthorizedComputingAccount' - required: - - data - type: object - AuthorizedComputingAccountTypeEnum: - enum: - - authorized-computing-accounts - type: string - AuthorizedLinkAccount: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_link_addons: - description: A related resource object from type configured-link-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - configured-link-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Configured link addons - type: object - external_link_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - external-link-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: External link service - type: object - required: - - external_link_service - type: object - type: - allOf: - - $ref: '#/components/schemas/AuthorizedLinkAccountTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - type: object - AuthorizedLinkAccountRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - minLength: 1 - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_link_addons: - description: A related resource object from type configured-link-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - configured-link-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - required: - - data - title: Configured link addons - type: object - external_link_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-link-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: External link service - type: object - required: - - external_link_service - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - authorized-link-accounts - type: string - required: - - type - type: object - required: - - data - type: object - AuthorizedLinkAccountResponse: - properties: - data: - $ref: '#/components/schemas/AuthorizedLinkAccount' - required: - - data - type: object - AuthorizedLinkAccountTypeEnum: - enum: - - authorized-link-accounts - type: string - AuthorizedStorageAccount: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - default_root_folder: - type: string - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - external_storage_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - external-storage-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: External storage service - type: object - required: - - external_storage_service - type: object - type: - allOf: - - $ref: '#/components/schemas/AuthorizedStorageAccountTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - type: object - AuthorizedStorageAccountRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - minLength: 1 - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - default_root_folder: - type: string - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - external_storage_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-storage-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: External storage service - type: object - required: - - external_storage_service - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - authorized-storage-accounts - type: string - required: - - type - type: object - required: - - data - type: object - AuthorizedStorageAccountResponse: - properties: - data: - $ref: '#/components/schemas/AuthorizedStorageAccount' - required: - - data - type: object - AuthorizedStorageAccountTypeEnum: - enum: - - authorized-storage-accounts - type: string - ConfiguredCitationAddon: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - root_folder: - type: string - required: - - connected_capabilities - type: object - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - authorized-citation-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_citation_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - external-citation-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: External citation service - type: object - required: - - base_account - type: object - type: - allOf: - - $ref: '#/components/schemas/ConfiguredCitationAddonTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - type: object - ConfiguredCitationAddonRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - minLength: 1 - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - minLength: 1 - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - root_folder: - type: string - required: - - connected_capabilities - type: object - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - authorized-citation-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_citation_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-citation-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: External citation service - type: object - required: - - base_account - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - configured-citation-addons - type: string - required: - - type - type: object - required: - - data - type: object - ConfiguredCitationAddonResponse: - properties: - data: - $ref: '#/components/schemas/ConfiguredCitationAddon' - required: - - data - type: object - ConfiguredCitationAddonTypeEnum: - enum: - - configured-citation-addons - type: string - ConfiguredComputingAddon: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - required: - - connected_capabilities - type: object - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - authorized-computing-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_computing_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - external-computing-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: External computing service - type: object - required: - - base_account - type: object - type: - allOf: - - $ref: '#/components/schemas/ConfiguredComputingAddonTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - type: object - ConfiguredComputingAddonRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - minLength: 1 - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - minLength: 1 - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - required: - - connected_capabilities - type: object - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - authorized-computing-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_computing_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-computing-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: External computing service - type: object - required: - - base_account - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - configured-computing-addons - type: string - required: - - type - type: object - required: - - data - type: object - ConfiguredComputingAddonResponse: - properties: - data: - $ref: '#/components/schemas/ConfiguredComputingAddon' - required: - - data - type: object - ConfiguredComputingAddonTypeEnum: - enum: - - configured-computing-addons - type: string - ConfiguredLinkAddon: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - resource_type: - description: '* `Audiovisual` - Audiovisual - - * `Award` - Award - - * `Book` - Book - - * `BookChapter` - BookChapter - - * `Collection` - Collection - - * `ComputationalNotebook` - ComputationalNotebook - - * `ConferencePaper` - ConferencePaper - - * `ConferenceProceeding` - ConferenceProceeding - - * `DataPaper` - DataPaper - - * `Dataset` - Dataset - - * `Dissertation` - Dissertation - - * `Event` - Event - - * `Image` - Image - - * `Instrument` - Instrument - - * `InteractiveResource` - InteractiveResource - - * `Journal` - Journal - - * `JournalArticle` - JournalArticle - - * `Model` - Model - - * `OutputManagementPlan` - OutputManagementPlan - - * `PeerReview` - PeerReview - - * `PhysicalObject` - PhysicalObject - - * `Preprint` - Preprint - - * `Project` - Project - - * `Report` - Report - - * `Service` - Service - - * `Software` - Software - - * `Sound` - Sound - - * `Standard` - Standard - - * `StudyRegistration` - StudyRegistration - - * `Text` - Text - - * `Workflow` - Workflow - - * `Other` - Other' - enum: - - Audiovisual - - Award - - Book - - BookChapter - - Collection - - ComputationalNotebook - - ConferencePaper - - ConferenceProceeding - - DataPaper - - Dataset - - Dissertation - - Event - - Image - - Instrument - - InteractiveResource - - Journal - - JournalArticle - - Model - - OutputManagementPlan - - PeerReview - - PhysicalObject - - Preprint - - Project - - Report - - Service - - Software - - Sound - - Standard - - StudyRegistration - - Text - - Workflow - - Other - - null - nullable: true - type: string - target_id: - nullable: true - type: string - target_url: - format: uri - readOnly: true - type: string - required: - - connected_capabilities - type: object - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - authorized-link-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_link_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - external-link-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: External link service - type: object - required: - - base_account - type: object - type: - allOf: - - $ref: '#/components/schemas/ConfiguredLinkAddonTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - type: object - ConfiguredLinkAddonRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - minLength: 1 - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - resource_type: - description: '* `Audiovisual` - Audiovisual - - * `Award` - Award - - * `Book` - Book - - * `BookChapter` - BookChapter - - * `Collection` - Collection - - * `ComputationalNotebook` - ComputationalNotebook - - * `ConferencePaper` - ConferencePaper - - * `ConferenceProceeding` - ConferenceProceeding - - * `DataPaper` - DataPaper - - * `Dataset` - Dataset - - * `Dissertation` - Dissertation - - * `Event` - Event - - * `Image` - Image - - * `Instrument` - Instrument - - * `InteractiveResource` - InteractiveResource - - * `Journal` - Journal - - * `JournalArticle` - JournalArticle - - * `Model` - Model - - * `OutputManagementPlan` - OutputManagementPlan - - * `PeerReview` - PeerReview - - * `PhysicalObject` - PhysicalObject - - * `Preprint` - Preprint - - * `Project` - Project - - * `Report` - Report - - * `Service` - Service - - * `Software` - Software - - * `Sound` - Sound - - * `Standard` - Standard - - * `StudyRegistration` - StudyRegistration - - * `Text` - Text - - * `Workflow` - Workflow - - * `Other` - Other' - enum: - - Audiovisual - - Award - - Book - - BookChapter - - Collection - - ComputationalNotebook - - ConferencePaper - - ConferenceProceeding - - DataPaper - - Dataset - - Dissertation - - Event - - Image - - Instrument - - InteractiveResource - - Journal - - JournalArticle - - Model - - OutputManagementPlan - - PeerReview - - PhysicalObject - - Preprint - - Project - - Report - - Service - - Software - - Sound - - Standard - - StudyRegistration - - Text - - Workflow - - Other - - null - nullable: true - type: string - target_id: - minLength: 1 - nullable: true - type: string - target_url: - format: uri - minLength: 1 - readOnly: true - type: string - required: - - connected_capabilities - type: object - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - authorized-link-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_link_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-link-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: External link service - type: object - required: - - base_account - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - configured-link-addons - type: string - required: - - type - type: object - required: - - data - type: object - ConfiguredLinkAddonResponse: - properties: - data: - $ref: '#/components/schemas/ConfiguredLinkAddon' - required: - - data - type: object - ConfiguredLinkAddonTypeEnum: - enum: - - configured-link-addons - type: string - ConfiguredStorageAddon: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - root_folder: - type: string - required: - - connected_capabilities - type: object - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - authorized-storage-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_storage_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - external-storage-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - readOnly: true - required: - - data - title: External storage service - type: object - required: - - base_account - type: object - type: - allOf: - - $ref: '#/components/schemas/ConfiguredStorageAddonTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - type: object - ConfiguredStorageAddonRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - minLength: 1 - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - root_folder: - type: string - required: - - connected_capabilities - type: object - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - authorized-storage-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_storage_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-storage-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: External storage service - type: object - required: - - base_account - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - configured-storage-addons - type: string - required: - - type - type: object - required: - - data - type: object - ConfiguredStorageAddonResponse: - properties: - data: - $ref: '#/components/schemas/ConfiguredStorageAddon' - required: - - data - type: object - ConfiguredStorageAddonTypeEnum: - enum: - - configured-storage-addons - type: string - ExternalCitationService: - additionalProperties: false - properties: - attributes: - properties: - api_base_url_options: - items: - type: string - readOnly: true - type: array - auth_uri: - readOnly: true - type: string - configurable_api_root: - readOnly: true - type: string - credentials_format: - description: '* `UNSPECIFIED` - UNSPECIFIED - - * `OAUTH2` - OAUTH2 - - * `ACCESS_KEY_SECRET_KEY` - ACCESS_KEY_SECRET_KEY - - * `USERNAME_PASSWORD` - USERNAME_PASSWORD - - * `PERSONAL_ACCESS_TOKEN` - PERSONAL_ACCESS_TOKEN - - * `OAUTH1A` - OAUTH1A - - * `DATAVERSE_API_TOKEN` - DATAVERSE_API_TOKEN' - enum: - - UNSPECIFIED - - OAUTH2 - - ACCESS_KEY_SECRET_KEY - - USERNAME_PASSWORD - - PERSONAL_ACCESS_TOKEN - - OAUTH1A - - DATAVERSE_API_TOKEN - readOnly: true - type: string - display_name: - type: string - external_service_name: - readOnly: true - type: string - icon_url: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - supported_features: - items: - description: '* `FORKING_PARTIAL` - FORKING_PARTIAL - - * `PERMISSIONS_PARTIAL` - PERMISSIONS_PARTIAL' - enum: - - FORKING_PARTIAL - - PERMISSIONS_PARTIAL - type: string - readOnly: true - type: array - wb_key: - type: string - required: - - display_name - type: object - id: {} - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - addon_imp: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-imps - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Addon imp - type: object - required: - - addon_imp - type: object - type: - allOf: - - $ref: '#/components/schemas/ExternalCitationServiceTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - - id - type: object - ExternalCitationServiceResponse: - properties: - data: - $ref: '#/components/schemas/ExternalCitationService' - required: - - data - type: object - ExternalCitationServiceTypeEnum: - enum: - - external-citation-services - type: string - ExternalComputingService: - additionalProperties: false - properties: - attributes: - properties: - api_base_url_options: - items: - type: string - readOnly: true - type: array - auth_uri: - readOnly: true - type: string - configurable_api_root: - readOnly: true - type: string - credentials_format: - description: '* `UNSPECIFIED` - UNSPECIFIED - - * `OAUTH2` - OAUTH2 - - * `ACCESS_KEY_SECRET_KEY` - ACCESS_KEY_SECRET_KEY - - * `USERNAME_PASSWORD` - USERNAME_PASSWORD - - * `PERSONAL_ACCESS_TOKEN` - PERSONAL_ACCESS_TOKEN - - * `OAUTH1A` - OAUTH1A - - * `DATAVERSE_API_TOKEN` - DATAVERSE_API_TOKEN' - enum: - - UNSPECIFIED - - OAUTH2 - - ACCESS_KEY_SECRET_KEY - - USERNAME_PASSWORD - - PERSONAL_ACCESS_TOKEN - - OAUTH1A - - DATAVERSE_API_TOKEN - readOnly: true - type: string - display_name: - type: string - icon_url: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - supported_features: - items: - description: '* `ADD_UPDATE_FILES_PARTIAL` - ADD_UPDATE_FILES_PARTIAL - - * `FORKING_PARTIAL` - FORKING_PARTIAL - - * `LOGS_PARTIAL` - LOGS_PARTIAL - - * `PERMISSIONS_PARTIAL` - PERMISSIONS_PARTIAL - - * `REGISTERING_PARTIAL` - REGISTERING_PARTIAL' - enum: - - ADD_UPDATE_FILES_PARTIAL - - FORKING_PARTIAL - - LOGS_PARTIAL - - PERMISSIONS_PARTIAL - - REGISTERING_PARTIAL - type: string - readOnly: true - type: array - wb_key: - type: string - required: - - display_name - type: object - id: {} - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - addon_imp: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-imps - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Addon imp - type: object - required: - - addon_imp - type: object - type: - allOf: - - $ref: '#/components/schemas/ExternalComputingServiceTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - - id - type: object - ExternalComputingServiceResponse: - properties: - data: - $ref: '#/components/schemas/ExternalComputingService' - required: - - data - type: object - ExternalComputingServiceTypeEnum: - enum: - - external-computing-services - type: string - ExternalLinkService: - additionalProperties: false - properties: - attributes: - properties: - api_base_url_options: - items: - type: string - readOnly: true - type: array - auth_uri: - readOnly: true - type: string - configurable_api_root: - readOnly: true - type: string - credentials_format: - description: '* `UNSPECIFIED` - UNSPECIFIED - - * `OAUTH2` - OAUTH2 - - * `ACCESS_KEY_SECRET_KEY` - ACCESS_KEY_SECRET_KEY - - * `USERNAME_PASSWORD` - USERNAME_PASSWORD - - * `PERSONAL_ACCESS_TOKEN` - PERSONAL_ACCESS_TOKEN - - * `OAUTH1A` - OAUTH1A - - * `DATAVERSE_API_TOKEN` - DATAVERSE_API_TOKEN' - enum: - - UNSPECIFIED - - OAUTH2 - - ACCESS_KEY_SECRET_KEY - - USERNAME_PASSWORD - - PERSONAL_ACCESS_TOKEN - - OAUTH1A - - DATAVERSE_API_TOKEN - readOnly: true - type: string - display_name: - type: string - external_service_name: - readOnly: true - type: string - icon_url: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - supported_features: - items: - description: '* `ADD_UPDATE_FILES` - ADD_UPDATE_FILES - - * `DELETE_FILES` - DELETE_FILES - - * `FORKING` - FORKING - - * `FORKING_PARTIAL` - FORKING_PARTIAL - - * `LOGS` - LOGS - - * `LOGS_PARTIAL` - LOGS_PARTIAL - - * `PERMISSIONS` - PERMISSIONS - - * `REGISTERING` - REGISTERING - - * `FILE_VERSIONS` - FILE_VERSIONS' - enum: - - ADD_UPDATE_FILES - - DELETE_FILES - - FORKING - - FORKING_PARTIAL - - LOGS - - LOGS_PARTIAL - - PERMISSIONS - - REGISTERING - - FILE_VERSIONS - type: string - readOnly: true - type: array - supported_resource_types: - items: - description: '* `Audiovisual` - Audiovisual - - * `Award` - Award - - * `Book` - Book - - * `BookChapter` - BookChapter - - * `Collection` - Collection - - * `ComputationalNotebook` - ComputationalNotebook - - * `ConferencePaper` - ConferencePaper - - * `ConferenceProceeding` - ConferenceProceeding - - * `DataPaper` - DataPaper - - * `Dataset` - Dataset - - * `Dissertation` - Dissertation - - * `Event` - Event - - * `Image` - Image - - * `Instrument` - Instrument - - * `InteractiveResource` - InteractiveResource - - * `Journal` - Journal - - * `JournalArticle` - JournalArticle - - * `Model` - Model - - * `OutputManagementPlan` - OutputManagementPlan - - * `PeerReview` - PeerReview - - * `PhysicalObject` - PhysicalObject - - * `Preprint` - Preprint - - * `Project` - Project - - * `Report` - Report - - * `Service` - Service - - * `Software` - Software - - * `Sound` - Sound - - * `Standard` - Standard - - * `StudyRegistration` - StudyRegistration - - * `Text` - Text - - * `Workflow` - Workflow - - * `Other` - Other' - enum: - - Audiovisual - - Award - - Book - - BookChapter - - Collection - - ComputationalNotebook - - ConferencePaper - - ConferenceProceeding - - DataPaper - - Dataset - - Dissertation - - Event - - Image - - Instrument - - InteractiveResource - - Journal - - JournalArticle - - Model - - OutputManagementPlan - - PeerReview - - PhysicalObject - - Preprint - - Project - - Report - - Service - - Software - - Sound - - Standard - - StudyRegistration - - Text - - Workflow - - Other - type: string - readOnly: true - type: array - wb_key: - type: string - required: - - display_name - type: object - id: {} - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - addon_imp: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-imps - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Addon imp - type: object - required: - - addon_imp - type: object - type: - allOf: - - $ref: '#/components/schemas/ExternalLinkServiceTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - - id - type: object - ExternalLinkServiceResponse: - properties: - data: - $ref: '#/components/schemas/ExternalLinkService' - required: - - data - type: object - ExternalLinkServiceTypeEnum: - enum: - - external-link-services - type: string - ExternalStorageService: - additionalProperties: false - properties: - attributes: - properties: - api_base_url_options: - items: - type: string - readOnly: true - type: array - auth_uri: - readOnly: true - type: string - configurable_api_root: - readOnly: true - type: string - credentials_format: - description: '* `UNSPECIFIED` - UNSPECIFIED - - * `OAUTH2` - OAUTH2 - - * `ACCESS_KEY_SECRET_KEY` - ACCESS_KEY_SECRET_KEY - - * `USERNAME_PASSWORD` - USERNAME_PASSWORD - - * `PERSONAL_ACCESS_TOKEN` - PERSONAL_ACCESS_TOKEN - - * `OAUTH1A` - OAUTH1A - - * `DATAVERSE_API_TOKEN` - DATAVERSE_API_TOKEN' - enum: - - UNSPECIFIED - - OAUTH2 - - ACCESS_KEY_SECRET_KEY - - USERNAME_PASSWORD - - PERSONAL_ACCESS_TOKEN - - OAUTH1A - - DATAVERSE_API_TOKEN - readOnly: true - type: string - display_name: - type: string - external_service_name: - readOnly: true - type: string - icon_url: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - max_concurrent_downloads: - maximum: 2147483647 - minimum: -2147483648 - type: integer - max_upload_mb: - maximum: 2147483647 - minimum: -2147483648 - type: integer - supported_features: - items: - description: '* `ADD_UPDATE_FILES` - ADD_UPDATE_FILES - - * `ADD_UPDATE_FILES_PARTIAL` - ADD_UPDATE_FILES_PARTIAL - - * `DELETE_FILES` - DELETE_FILES - - * `DELETE_FILES_PARTIAL` - DELETE_FILES_PARTIAL - - * `FORKING` - FORKING - - * `LOGS` - LOGS - - * `PERMISSIONS` - PERMISSIONS - - * `REGISTERING` - REGISTERING - - * `FILE_VERSIONS` - FILE_VERSIONS - - * `COPY_INTO` - COPY_INTO - - * `DOWNLOAD_AS_ZIP` - DOWNLOAD_AS_ZIP' - enum: - - ADD_UPDATE_FILES - - ADD_UPDATE_FILES_PARTIAL - - DELETE_FILES - - DELETE_FILES_PARTIAL - - FORKING - - LOGS - - PERMISSIONS - - REGISTERING - - FILE_VERSIONS - - COPY_INTO - - DOWNLOAD_AS_ZIP - type: string - readOnly: true - type: array - wb_key: - type: string - required: - - max_concurrent_downloads - - max_upload_mb - - display_name - type: object - id: {} - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - addon_imp: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - addon-imps - title: Resource Type Name - type: string - required: - - id - - type - type: object - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Addon imp - type: object - required: - - addon_imp - type: object - type: - allOf: - - $ref: '#/components/schemas/ExternalStorageServiceTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - - id - type: object - ExternalStorageServiceResponse: - properties: - data: - $ref: '#/components/schemas/ExternalStorageService' - required: - - data - type: object - ExternalStorageServiceTypeEnum: - enum: - - external-storage-services - type: string - PaginatedExternalCitationServiceList: - properties: - data: - items: - $ref: '#/components/schemas/ExternalCitationService' - type: array - links: - properties: - first: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - last: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - next: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - prev: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - required: - - count - - next - - prev - type: object - meta: - properties: - pagination: - properties: - count: - example: 2 - type: integer - page: - example: 1 - type: integer - pages: - example: 1 - type: integer - required: - - count - - next - - prev - type: object - required: - - pagination - type: object - required: - - meta - - data - - links - type: object - PaginatedExternalComputingServiceList: - properties: - data: - items: - $ref: '#/components/schemas/ExternalComputingService' - type: array - links: - properties: - first: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - last: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - next: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - prev: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - required: - - count - - next - - prev - type: object - meta: - properties: - pagination: - properties: - count: - example: 2 - type: integer - page: - example: 1 - type: integer - pages: - example: 1 - type: integer - required: - - count - - next - - prev - type: object - required: - - pagination - type: object - required: - - meta - - data - - links - type: object - PaginatedExternalLinkServiceList: - properties: - data: - items: - $ref: '#/components/schemas/ExternalLinkService' - type: array - links: - properties: - first: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - last: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - next: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - prev: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - required: - - count - - next - - prev - type: object - meta: - properties: - pagination: - properties: - count: - example: 2 - type: integer - page: - example: 1 - type: integer - pages: - example: 1 - type: integer - required: - - count - - next - - prev - type: object - required: - - pagination - type: object - required: - - meta - - data - - links - type: object - PaginatedExternalStorageServiceList: - properties: - data: - items: - $ref: '#/components/schemas/ExternalStorageService' - type: array - links: - properties: - first: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - last: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - next: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - prev: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - required: - - count - - next - - prev - type: object - meta: - properties: - pagination: - properties: - count: - example: 2 - type: integer - page: - example: 1 - type: integer - pages: - example: 1 - type: integer - required: - - count - - next - - prev - type: object - required: - - pagination - type: object - required: - - meta - - data - - links - type: object - PaginatedResourceReferenceList: - properties: - data: - items: - $ref: '#/components/schemas/ResourceReference' - type: array - links: - properties: - first: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - last: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - next: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - prev: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - required: - - count - - next - - prev - type: object - meta: - properties: - pagination: - properties: - count: - example: 2 - type: integer - page: - example: 1 - type: integer - pages: - example: 1 - type: integer - required: - - count - - next - - prev - type: object - required: - - pagination - type: object - required: - - meta - - data - - links - type: object - PaginatedUserReferenceList: - properties: - data: - items: - $ref: '#/components/schemas/UserReference' - type: array - links: - properties: - first: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - last: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - next: - example: http://api.example.org/accounts/?page[number]=3 - format: uri - nullable: true - type: string - prev: - example: http://api.example.org/accounts/?page[number]=1 - format: uri - nullable: true - type: string - required: - - count - - next - - prev - type: object - meta: - properties: - pagination: - properties: - count: - example: 2 - type: integer - page: - example: 1 - type: integer - pages: - example: 1 - type: integer - required: - - count - - next - - prev - type: object - required: - - pagination - type: object - required: - - meta - - data - - links - type: object - PatchedAuthorizedCitationAccountRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - minLength: 1 - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - default_root_folder: - type: string - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - id: {} - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_citation_addons: - description: A related resource object from type configured-citation-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - configured-citation-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - required: - - data - title: Configured citation addons - type: object - external_citation_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-citation-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: External citation service - type: object - required: - - external_citation_service - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - authorized-citation-accounts - type: string - required: - - type - - id - type: object - required: - - data - type: object - PatchedAuthorizedComputingAccountRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - minLength: 1 - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - id: {} - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_computing_addons: - description: A related resource object from type configured-computing-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - configured-computing-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - required: - - data - title: Configured computing addons - type: object - external_computing_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-computing-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: External computing service - type: object - required: - - external_computing_service - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - authorized-computing-accounts - type: string - required: - - type - - id - type: object - required: - - data - type: object - PatchedAuthorizedLinkAccountRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - minLength: 1 - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - id: {} - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_link_addons: - description: A related resource object from type configured-link-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - configured-link-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - required: - - data - title: Configured link addons - type: object - external_link_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-link-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: External link service - type: object - required: - - external_link_service - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - authorized-link-accounts - type: string - required: - - type - - id - type: object - required: - - data - type: object - PatchedAuthorizedStorageAccountRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - api_base_url: - format: uri - nullable: true - type: string - auth_url: - minLength: 1 - readOnly: true - type: string - authorized_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - authorized_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - credentials: - writeOnly: true - credentials_available: - readOnly: true - type: boolean - default_root_folder: - type: string - display_name: - maxLength: 256 - nullable: true - type: string - id: - format: uuid - readOnly: true - type: string - initiate_oauth: - type: boolean - writeOnly: true - required: - - authorized_capabilities - type: object - id: {} - relationships: - properties: - account_owner: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - user-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Account owner - type: object - authorized_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized operations - type: object - configured_storage_addons: - description: A related resource object from type configured-storage-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - configured-storage-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - required: - - data - title: Configured storage addons - type: object - external_storage_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-storage-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: External storage service - type: object - required: - - external_storage_service - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - authorized-storage-accounts - type: string - required: - - type - - id - type: object - required: - - data - type: object - PatchedConfiguredCitationAddonRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - minLength: 1 - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - minLength: 1 - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - root_folder: - type: string - required: - - connected_capabilities - type: object - id: {} - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - authorized-citation-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_citation_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-citation-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: External citation service - type: object - required: - - base_account - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - configured-citation-addons - type: string - required: - - type - - id - type: object - required: - - data - type: object - PatchedConfiguredComputingAddonRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - minLength: 1 - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - minLength: 1 - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - required: - - connected_capabilities - type: object - id: {} - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - authorized-computing-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_computing_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-computing-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: External computing service - type: object - required: - - base_account - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - configured-computing-addons - type: string - required: - - type - - id - type: object - required: - - data - type: object - PatchedConfiguredLinkAddonRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - minLength: 1 - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - resource_type: - description: '* `Audiovisual` - Audiovisual - - * `Award` - Award - - * `Book` - Book - - * `BookChapter` - BookChapter - - * `Collection` - Collection - - * `ComputationalNotebook` - ComputationalNotebook - - * `ConferencePaper` - ConferencePaper - - * `ConferenceProceeding` - ConferenceProceeding - - * `DataPaper` - DataPaper - - * `Dataset` - Dataset - - * `Dissertation` - Dissertation - - * `Event` - Event - - * `Image` - Image - - * `Instrument` - Instrument - - * `InteractiveResource` - InteractiveResource - - * `Journal` - Journal - - * `JournalArticle` - JournalArticle - - * `Model` - Model - - * `OutputManagementPlan` - OutputManagementPlan - - * `PeerReview` - PeerReview - - * `PhysicalObject` - PhysicalObject - - * `Preprint` - Preprint - - * `Project` - Project - - * `Report` - Report - - * `Service` - Service - - * `Software` - Software - - * `Sound` - Sound - - * `Standard` - Standard - - * `StudyRegistration` - StudyRegistration - - * `Text` - Text - - * `Workflow` - Workflow - - * `Other` - Other' - enum: - - Audiovisual - - Award - - Book - - BookChapter - - Collection - - ComputationalNotebook - - ConferencePaper - - ConferenceProceeding - - DataPaper - - Dataset - - Dissertation - - Event - - Image - - Instrument - - InteractiveResource - - Journal - - JournalArticle - - Model - - OutputManagementPlan - - PeerReview - - PhysicalObject - - Preprint - - Project - - Report - - Service - - Software - - Sound - - Standard - - StudyRegistration - - Text - - Workflow - - Other - - null - nullable: true - type: string - target_id: - minLength: 1 - nullable: true - type: string - target_url: - format: uri - minLength: 1 - readOnly: true - type: string - required: - - connected_capabilities - type: object - id: {} - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - authorized-link-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_link_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-link-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: External link service - type: object - required: - - base_account - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - configured-link-addons - type: string - required: - - type - - id - type: object - required: - - data - type: object - PatchedConfiguredStorageAddonRequest: - properties: - data: - additionalProperties: false - properties: - attributes: - properties: - authorized_resource_uri: - minLength: 1 - type: string - writeOnly: true - connected_capabilities: - items: - description: '* `ACCESS` - ACCESS - - * `UPDATE` - UPDATE' - enum: - - ACCESS - - UPDATE - type: string - type: array - connected_operation_names: - items: - minLength: 1 - type: string - readOnly: true - type: array - current_user_is_owner: - readOnly: true - type: boolean - display_name: - maxLength: 256 - nullable: true - type: string - external_service_name: - readOnly: true - type: string - id: - format: uuid - readOnly: true - type: string - root_folder: - type: string - required: - - connected_capabilities - type: object - id: {} - relationships: - properties: - authorized_resource: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Authorized resource - type: object - base_account: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - authorized-storage-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - required: - - data - title: Base account - type: object - connected_operations: - description: The identifier of the related object. - properties: - data: - properties: - id: - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - addon-operations - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: Connected operations - type: object - external_storage_service: - description: The identifier of the related object. - properties: - data: - properties: - id: - format: uuid - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share - common attributes and relationships. - enum: - - external-storage-services - title: Resource Type Name - type: string - required: - - id - - type - type: object - readOnly: true - required: - - data - title: External storage service - type: object - required: - - base_account - type: object - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - enum: - - configured-storage-addons - type: string - required: - - type - - id - type: object - required: - - data - type: object - ResourceReference: - additionalProperties: false - properties: - attributes: - properties: - resource_uri: - format: uri - maxLength: 200 - type: string - required: - - resource_uri - type: object - id: - format: uuid - type: string - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - configured_citation_addons: - description: A related resource object from type configured-citation-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - configured-citation-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Configured citation addons - type: object - configured_computing_addons: - description: A related resource object from type configured-computing-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - configured-computing-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Configured computing addons - type: object - configured_link_addons: - description: A related resource object from type configured-link-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - configured-link-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Configured link addons - type: object - configured_storage_addons: - description: A related resource object from type configured-storage-addons - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - configured-storage-addons - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Configured storage addons - type: object - required: - - configured_storage_addons - - configured_link_addons - - configured_citation_addons - - configured_computing_addons - type: object - type: - allOf: - - $ref: '#/components/schemas/ResourceReferenceTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - - id - type: object - ResourceReferenceResponse: - properties: - data: - $ref: '#/components/schemas/ResourceReference' - required: - - data - type: object - ResourceReferenceTypeEnum: - enum: - - resource-references - type: string - UserReference: - additionalProperties: false - properties: - attributes: - properties: - user_uri: - format: uri - maxLength: 200 - type: string - required: - - user_uri - type: object - id: - format: uuid - type: string - links: - properties: - self: - example: http://api.example.org/accounts/123 - format: uri - nullable: false - type: string - type: object - relationships: - properties: - authorized_citation_accounts: - description: A related resource object from type authorized-citation-accounts - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - authorized-citation-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Authorized citation accounts - type: object - authorized_computing_accounts: - description: A related resource object from type authorized-computing-accounts - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - authorized-computing-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Authorized computing accounts - type: object - authorized_link_accounts: - description: A related resource object from type authorized-citation-accounts - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - authorized-citation-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Authorized link accounts - type: object - authorized_storage_accounts: - description: A related resource object from type authorized-storage-accounts - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - authorized-storage-accounts - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Authorized storage accounts - type: object - configured_resources: - description: A related resource object from type resource-references - properties: - data: - items: - properties: - id: - description: The identifier of the related object. - title: Resource Identifier - type: string - type: - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common - attributes and relationships. - enum: - - resource-references - title: Resource Type Name - type: string - required: - - id - - type - type: object - type: array - links: - properties: - related: - example: http://api.example.org/accounts/123 - format: uri - nullable: true - type: string - type: object - required: - - data - title: Configured resources - type: object - required: - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: object - type: - allOf: - - $ref: '#/components/schemas/UserReferenceTypeEnum' - description: The [type](https://jsonapi.org/format/#document-resource-object-identification) - member is used to describe resource objects that share common attributes - and relationships. - required: - - type - - id - type: object - UserReferenceResponse: - properties: - data: - $ref: '#/components/schemas/UserReference' - required: - - data - type: object - UserReferenceTypeEnum: - enum: - - user-references - type: string -info: - description: Addons service designed for use with OSF - title: GravyValet API - version: 1.0.0 -openapi: 3.0.3 -paths: - /v1/addon-imps/: - get: - description: viewset for read-only access to any `StaticDataclassModel` - operationId: addon_imps_list - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-imps] - schema: - items: - enum: - - url - - name - - docstring - - interface_docstring - - implemented_operations - type: string - type: array - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonImpResponse' - description: '' - security: - - {} - tags: - - addon-imps - /v1/addon-imps/{id}/: - get: - description: viewset for read-only access to any `StaticDataclassModel` - operationId: addon_imps_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-imps] - schema: - items: - enum: - - url - - name - - docstring - - interface_docstring - - implemented_operations - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonImpResponse' - description: '' - security: - - {} - tags: - - addon-imps - /v1/addon-imps/{id}/implemented_operations: - get: - description: Fetch all related AddonOperation to this AddonImp - operationId: addon_imps_retrieve_2_related_implemented_operations - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: - - addon-imps - /v1/addon-operation-invocations/: - post: - description: Perform some action using external service, for instance list files - on storage provider. In order to perform such action you need to include configured_addon - relationship - operationId: addon_operation_invocations_create - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationInvocationRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/AddonOperationInvocationRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/AddonOperationInvocationRequest' - required: true - responses: - '201': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationInvocationResponse' - description: '' - tags: - - addon-operation-invocations - /v1/addon-operation-invocations/{id}/: - get: - description: Get singular instance of addon operation invocation by it's pk. - May be useful to view action log - operationId: addon_operation_invocations_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operation-invocations] - schema: - items: - enum: - - url - - invocation_status - - operation_kwargs - - operation_result - - operation - - by_user - - thru_account - - thru_addon - - created - - modified - - operation_name - type: string - type: array - - description: A UUID string identifying this addon operation invocation. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - thru_account - - thru_addon - - operation - - by_user - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationInvocationResponse' - description: '' - tags: - - addon-operation-invocations - /v1/addon-operation-invocations/{id}/by_user: - get: - description: Fetch all related UserReference to this AddonOperationInvocation - operationId: addon_operation_invocations_retrieve_2_related_by_user - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[user-references] - schema: - items: - enum: - - url - - user_uri - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - - description: A UUID string identifying this User Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/UserReferenceResponse' - description: '' - tags: &id001 - - addon-operation-invocations - /v1/addon-operation-invocations/{id}/operation: - get: - description: Fetch all related AddonOperation to this AddonOperationInvocation - operationId: addon_operation_invocations_retrieve_2_related_operation - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: *id001 - /v1/addon-operation-invocations/{id}/thru_account: - get: - description: Fetch all related AuthorizedStorageAccount to this AddonOperationInvocation - operationId: addon_operation_invocations_retrieve_2_related_thru_account - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-storage-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_storage_addons - - credentials - - default_root_folder - - external_storage_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Storage Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_storage_service - - configured_storage_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedStorageAccountResponse' - description: '' - tags: *id001 - /v1/addon-operation-invocations/{id}/thru_addon: - get: - description: Fetch all related ConfiguredStorageAddon to this AddonOperationInvocation - operationId: addon_operation_invocations_retrieve_2_related_thru_addon - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-storage-addons] - schema: - items: - enum: - - id - - url - - display_name - - root_folder - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_storage_service - - current_user_is_owner - - external_service_name - type: string - type: array - - description: A UUID string identifying this Configured Storage Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_storage_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredStorageAddonResponse' - description: '' - tags: *id001 - /v1/addon-operations/: - get: - description: viewset for read-only access to any `StaticDataclassModel` - operationId: addon_operations_list - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: - - addon-operations - /v1/addon-operations/{id}/: - get: - description: viewset for read-only access to any `StaticDataclassModel` - operationId: addon_operations_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: - - addon-operations - /v1/addon-operations/{id}/implemented_by: - get: - description: Fetch all related AddonImp to this AddonOperation - operationId: addon_operations_retrieve_2_related_implemented_by - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-imps] - schema: - items: - enum: - - url - - name - - docstring - - interface_docstring - - implemented_operations - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonImpResponse' - description: '' - security: - - {} - tags: - - addon-operations - /v1/authorized-citation-accounts/: - post: - description: "Create new authorized citation account for given external citation\ - \ service.\n For OAuth services it's required to create account with `\"initiate_oauth\"\ - =true` in order to proceed with OAuth flow" - operationId: authorized_citation_accounts_create - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedCitationAccountRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/AuthorizedCitationAccountRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/AuthorizedCitationAccountRequest' - required: true - responses: - '201': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedCitationAccountResponse' - description: '' - tags: - - authorized-citation-accounts - /v1/authorized-citation-accounts/{id}/: - delete: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_citation_accounts_destroy - parameters: - - description: A UUID string identifying this Authorized Citation Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - responses: - '204': - description: No response body - tags: - - authorized-citation-accounts - get: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_citation_accounts_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-citation-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_citation_addons - - credentials - - default_root_folder - - external_citation_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Citation Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_citation_service - - configured_citation_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedCitationAccountResponse' - description: '' - tags: - - authorized-citation-accounts - patch: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_citation_accounts_partial_update - parameters: - - description: A UUID string identifying this Authorized Citation Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PatchedAuthorizedCitationAccountRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedAuthorizedCitationAccountRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedAuthorizedCitationAccountRequest' - required: true - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedCitationAccountResponse' - description: '' - tags: - - authorized-citation-accounts - /v1/authorized-citation-accounts/{id}/account_owner: - get: - description: Fetch all related UserReference to this AuthorizedCitationAccount - operationId: authorized_citation_accounts_retrieve_2_related_account_owner - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[user-references] - schema: - items: - enum: - - url - - user_uri - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - - description: A UUID string identifying this User Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/UserReferenceResponse' - description: '' - tags: &id002 - - authorized-citation-accounts - /v1/authorized-citation-accounts/{id}/authorized_operations: - get: - description: Fetch all related AddonOperation to this AuthorizedCitationAccount - operationId: authorized_citation_accounts_retrieve_2_related_authorized_operations - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: *id002 - /v1/authorized-citation-accounts/{id}/configured_citation_addons: - get: - description: Fetch all related ConfiguredCitationAddon to this AuthorizedCitationAccount - operationId: authorized_citation_accounts_retrieve_2_related_configured_citation_addons - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-citation-addons] - schema: - items: - enum: - - id - - url - - display_name - - root_folder - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_service_name - - external_citation_service - - current_user_is_owner - type: string - type: array - - description: A UUID string identifying this Configured Citation Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_citation_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredCitationAddonResponse' - description: '' - tags: *id002 - /v1/authorized-citation-accounts/{id}/external_citation_service: - get: - description: Fetch all related ExternalCitationService to this AuthorizedCitationAccount - operationId: authorized_citation_accounts_retrieve_2_related_external_citation_service - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-citation-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - configurable_api_root - - wb_key - - external_service_name - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Citation Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalCitationServiceResponse' - description: '' - security: - - {} - tags: *id002 - /v1/authorized-computing-accounts/: - post: - description: "Create new authorized computing account for given external computing\ - \ service.\n For OAuth services it's required to create account with `\"initiate_oauth\"\ - =true` in order to proceed with OAuth flow" - operationId: authorized_computing_accounts_create - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedComputingAccountRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/AuthorizedComputingAccountRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/AuthorizedComputingAccountRequest' - required: true - responses: - '201': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedComputingAccountResponse' - description: '' - tags: - - authorized-computing-accounts - /v1/authorized-computing-accounts/{id}/: - delete: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_computing_accounts_destroy - parameters: - - description: A UUID string identifying this Authorized Computing Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - responses: - '204': - description: No response body - tags: - - authorized-computing-accounts - get: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_computing_accounts_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-computing-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_computing_addons - - credentials - - external_computing_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Computing Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_computing_service - - configured_computing_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedComputingAccountResponse' - description: '' - tags: - - authorized-computing-accounts - patch: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_computing_accounts_partial_update - parameters: - - description: A UUID string identifying this Authorized Computing Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PatchedAuthorizedComputingAccountRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedAuthorizedComputingAccountRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedAuthorizedComputingAccountRequest' - required: true - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedComputingAccountResponse' - description: '' - tags: - - authorized-computing-accounts - /v1/authorized-computing-accounts/{id}/account_owner: - get: - description: Fetch all related UserReference to this AuthorizedComputingAccount - operationId: authorized_computing_accounts_retrieve_2_related_account_owner - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[user-references] - schema: - items: - enum: - - url - - user_uri - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - - description: A UUID string identifying this User Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/UserReferenceResponse' - description: '' - tags: &id003 - - authorized-computing-accounts - /v1/authorized-computing-accounts/{id}/authorized_operations: - get: - description: Fetch all related AddonOperation to this AuthorizedComputingAccount - operationId: authorized_computing_accounts_retrieve_2_related_authorized_operations - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: *id003 - /v1/authorized-computing-accounts/{id}/configured_computing_addons: - get: - description: Fetch all related ConfiguredComputingAddon to this AuthorizedComputingAccount - operationId: authorized_computing_accounts_retrieve_2_related_configured_computing_addons - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-computing-addons] - schema: - items: - enum: - - id - - url - - display_name - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_service_name - - external_computing_service - - current_user_is_owner - type: string - type: array - - description: A UUID string identifying this Configured Computing Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_computing_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredComputingAddonResponse' - description: '' - tags: *id003 - /v1/authorized-computing-accounts/{id}/external_computing_service: - get: - description: Fetch all related ExternalComputingService to this AuthorizedComputingAccount - operationId: authorized_computing_accounts_retrieve_2_related_external_computing_service - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-computing-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - configurable_api_root - - supported_features - - icon_url - - wb_key - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Computing Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalComputingServiceResponse' - description: '' - security: - - {} - tags: *id003 - /v1/authorized-link-accounts/: - post: - description: "Create new authorized link account for given external link service.\n\ - \ For OAuth services it's required to create account with `\"initiate_oauth\"\ - =true` in order to proceed with OAuth flow" - operationId: authorized_link_accounts_create - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedLinkAccountRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/AuthorizedLinkAccountRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/AuthorizedLinkAccountRequest' - required: true - responses: - '201': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedLinkAccountResponse' - description: '' - tags: - - authorized-link-accounts - /v1/authorized-link-accounts/{id}/: - delete: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_link_accounts_destroy - parameters: - - description: A UUID string identifying this Authorized Link Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - responses: - '204': - description: No response body - tags: - - authorized-link-accounts - get: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_link_accounts_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-link-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_link_addons - - credentials - - external_link_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Link Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_link_service - - configured_link_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedLinkAccountResponse' - description: '' - tags: - - authorized-link-accounts - patch: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_link_accounts_partial_update - parameters: - - description: A UUID string identifying this Authorized Link Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PatchedAuthorizedLinkAccountRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedAuthorizedLinkAccountRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedAuthorizedLinkAccountRequest' - required: true - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedLinkAccountResponse' - description: '' - tags: - - authorized-link-accounts - /v1/authorized-link-accounts/{id}/account_owner: - get: - description: Fetch all related UserReference to this AuthorizedLinkAccount - operationId: authorized_link_accounts_retrieve_2_related_account_owner - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[user-references] - schema: - items: - enum: - - url - - user_uri - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - - description: A UUID string identifying this User Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/UserReferenceResponse' - description: '' - tags: &id004 - - authorized-link-accounts - /v1/authorized-link-accounts/{id}/authorized_operations: - get: - description: Fetch all related AddonOperation to this AuthorizedLinkAccount - operationId: authorized_link_accounts_retrieve_2_related_authorized_operations - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: *id004 - /v1/authorized-link-accounts/{id}/configured_link_addons: - get: - description: Fetch all related ConfiguredLinkAddon to this AuthorizedLinkAccount - operationId: authorized_link_accounts_retrieve_2_related_configured_link_addons - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-link-addons] - schema: - items: - enum: - - id - - display_name - - target_url - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_link_service - - current_user_is_owner - - external_service_name - - resource_type - - target_id - type: string - type: array - - description: A UUID string identifying this Configured Link Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_link_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredLinkAddonResponse' - description: '' - tags: *id004 - /v1/authorized-link-accounts/{id}/external_link_service: - get: - description: Fetch all related ExternalLinkService to this AuthorizedLinkAccount - operationId: authorized_link_accounts_retrieve_2_related_external_link_service - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-link-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - wb_key - - external_service_name - - configurable_api_root - - supported_resource_types - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Link Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalLinkServiceResponse' - description: '' - security: - - {} - tags: *id004 - /v1/authorized-storage-accounts/: - post: - description: "Create new authorized storage account for given external storage\ - \ service.\n For OAuth services it's required to create account with `\"initiate_oauth\"\ - =true` in order to proceed with OAuth flow" - operationId: authorized_storage_accounts_create - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedStorageAccountRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/AuthorizedStorageAccountRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/AuthorizedStorageAccountRequest' - required: true - responses: - '201': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedStorageAccountResponse' - description: '' - tags: - - authorized-storage-accounts - /v1/authorized-storage-accounts/{id}/: - delete: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_storage_accounts_destroy - parameters: - - description: A UUID string identifying this Authorized Storage Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - responses: - '204': - description: No response body - tags: - - authorized-storage-accounts - get: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_storage_accounts_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-storage-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_storage_addons - - credentials - - default_root_folder - - external_storage_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Storage Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_storage_service - - configured_storage_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedStorageAccountResponse' - description: '' - tags: - - authorized-storage-accounts - patch: - description: viewset allowing create, retrieve, update, delete - operationId: authorized_storage_accounts_partial_update - parameters: - - description: A UUID string identifying this Authorized Storage Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PatchedAuthorizedStorageAccountRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedAuthorizedStorageAccountRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedAuthorizedStorageAccountRequest' - required: true - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedStorageAccountResponse' - description: '' - tags: - - authorized-storage-accounts - /v1/authorized-storage-accounts/{id}/account_owner: - get: - description: Fetch all related UserReference to this AuthorizedStorageAccount - operationId: authorized_storage_accounts_retrieve_2_related_account_owner - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[user-references] - schema: - items: - enum: - - url - - user_uri - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - - description: A UUID string identifying this User Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/UserReferenceResponse' - description: '' - tags: &id005 - - authorized-storage-accounts - /v1/authorized-storage-accounts/{id}/authorized_operations: - get: - description: Fetch all related AddonOperation to this AuthorizedStorageAccount - operationId: authorized_storage_accounts_retrieve_2_related_authorized_operations - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: *id005 - /v1/authorized-storage-accounts/{id}/external_storage_service: - get: - description: Fetch all related ExternalStorageService to this AuthorizedStorageAccount - operationId: authorized_storage_accounts_retrieve_2_related_external_storage_service - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-storage-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - max_concurrent_downloads - - max_upload_mb - - display_name - - url - - wb_key - - external_service_name - - configurable_api_root - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Storage Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalStorageServiceResponse' - description: '' - security: - - {} - tags: *id005 - /v1/configured-citation-addons/: - post: - description: "Create new configured citation addon for given authorized citation\ - \ account, linking it to desired project.\n To configure it properly, you\ - \ must specify `root_folder` on the provider's side.\n Note that everything\ - \ under this folder is going to be accessible to everyone who has access to\ - \ this project" - operationId: configured_citation_addons_create - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredCitationAddonRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ConfiguredCitationAddonRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/ConfiguredCitationAddonRequest' - required: true - responses: - '201': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredCitationAddonResponse' - description: '' - tags: - - configured-citation-addons - /v1/configured-citation-addons/{id}/: - delete: - description: viewset allowing create, retrieve, update, delete - operationId: configured_citation_addons_destroy - parameters: - - description: A UUID string identifying this Configured Citation Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - responses: - '204': - description: No response body - tags: - - configured-citation-addons - get: - description: viewset allowing create, retrieve, update, delete - operationId: configured_citation_addons_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-citation-addons] - schema: - items: - enum: - - id - - url - - display_name - - root_folder - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_service_name - - external_citation_service - - current_user_is_owner - type: string - type: array - - description: A UUID string identifying this Configured Citation Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_citation_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredCitationAddonResponse' - description: '' - tags: - - configured-citation-addons - patch: - description: viewset allowing create, retrieve, update, delete - operationId: configured_citation_addons_partial_update - parameters: - - description: A UUID string identifying this Configured Citation Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PatchedConfiguredCitationAddonRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedConfiguredCitationAddonRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedConfiguredCitationAddonRequest' - required: true - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredCitationAddonResponse' - description: '' - tags: - - configured-citation-addons - /v1/configured-citation-addons/{id}/authorized_resource: - get: - description: Fetch all related ResourceReference to this ConfiguredCitationAddon - operationId: configured_citation_addons_retrieve_2_related_authorized_resource - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[resource-references] - schema: - items: - enum: - - url - - resource_uri - - configured_storage_addons - - configured_link_addons - - configured_citation_addons - - configured_computing_addons - type: string - type: array - - description: A UUID string identifying this Resource Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - configured_storage_addons - - configured_citation_addons - - configured_link_addons - - configured_computing_addons - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ResourceReferenceResponse' - description: '' - tags: &id006 - - configured-citation-addons - /v1/configured-citation-addons/{id}/base_account: - get: - description: Fetch all related AuthorizedCitationAccount to this ConfiguredCitationAddon - operationId: configured_citation_addons_retrieve_2_related_base_account - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-citation-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_citation_addons - - credentials - - default_root_folder - - external_citation_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Citation Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_citation_service - - configured_citation_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedCitationAccountResponse' - description: '' - tags: *id006 - /v1/configured-citation-addons/{id}/connected_operations: - get: - description: Fetch all related AddonOperation to this ConfiguredCitationAddon - operationId: configured_citation_addons_retrieve_2_related_connected_operations - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: *id006 - /v1/configured-citation-addons/{id}/external_citation_service: - get: - description: Fetch all related ExternalCitationService to this ConfiguredCitationAddon - operationId: configured_citation_addons_retrieve_2_related_external_citation_service - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-citation-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - configurable_api_root - - wb_key - - external_service_name - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Citation Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalCitationServiceResponse' - description: '' - security: - - {} - tags: *id006 - /v1/configured-computing-addons/: - post: - description: "Create new configured computing addon for given authorized computing\ - \ account, linking it to desired project.\n To configure it properly, you\ - \ must specify `root_folder` on the provider's side.\n Note that everything\ - \ under this folder is going to be accessible to everyone who has access to\ - \ this project" - operationId: configured_computing_addons_create - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredComputingAddonRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ConfiguredComputingAddonRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/ConfiguredComputingAddonRequest' - required: true - responses: - '201': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredComputingAddonResponse' - description: '' - tags: - - configured-computing-addons - /v1/configured-computing-addons/{id}/: - delete: - description: viewset allowing create, retrieve, update, delete - operationId: configured_computing_addons_destroy - parameters: - - description: A UUID string identifying this Configured Computing Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - responses: - '204': - description: No response body - tags: - - configured-computing-addons - get: - description: viewset allowing create, retrieve, update, delete - operationId: configured_computing_addons_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-computing-addons] - schema: - items: - enum: - - id - - url - - display_name - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_service_name - - external_computing_service - - current_user_is_owner - type: string - type: array - - description: A UUID string identifying this Configured Computing Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_computing_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredComputingAddonResponse' - description: '' - tags: - - configured-computing-addons - patch: - description: viewset allowing create, retrieve, update, delete - operationId: configured_computing_addons_partial_update - parameters: - - description: A UUID string identifying this Configured Computing Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PatchedConfiguredComputingAddonRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedConfiguredComputingAddonRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedConfiguredComputingAddonRequest' - required: true - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredComputingAddonResponse' - description: '' - tags: - - configured-computing-addons - /v1/configured-computing-addons/{id}/authorized_resource: - get: - description: Fetch all related ResourceReference to this ConfiguredComputingAddon - operationId: configured_computing_addons_retrieve_2_related_authorized_resource - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[resource-references] - schema: - items: - enum: - - url - - resource_uri - - configured_storage_addons - - configured_link_addons - - configured_citation_addons - - configured_computing_addons - type: string - type: array - - description: A UUID string identifying this Resource Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - configured_storage_addons - - configured_citation_addons - - configured_link_addons - - configured_computing_addons - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ResourceReferenceResponse' - description: '' - tags: &id007 - - configured-computing-addons - /v1/configured-computing-addons/{id}/base_account: - get: - description: Fetch all related AuthorizedComputingAccount to this ConfiguredComputingAddon - operationId: configured_computing_addons_retrieve_2_related_base_account - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-computing-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_computing_addons - - credentials - - external_computing_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Computing Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_computing_service - - configured_computing_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedComputingAccountResponse' - description: '' - tags: *id007 - /v1/configured-computing-addons/{id}/connected_operations: - get: - description: Fetch all related AddonOperation to this ConfiguredComputingAddon - operationId: configured_computing_addons_retrieve_2_related_connected_operations - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: *id007 - /v1/configured-computing-addons/{id}/external_computing_service: - get: - description: Fetch all related ExternalComputingService to this ConfiguredComputingAddon - operationId: configured_computing_addons_retrieve_2_related_external_computing_service - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-computing-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - configurable_api_root - - supported_features - - icon_url - - wb_key - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Computing Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalComputingServiceResponse' - description: '' - security: - - {} - tags: *id007 - /v1/configured-link-addons/: - post: - description: "Create new configured link addon for given authorized link account,\ - \ linking it to desired project.\n To configure it properly, you must specify\ - \ `root_folder` on the provider's side.\n Note that everything under this\ - \ folder is going to be accessible to everyone who has access to this project" - operationId: configured_link_addons_create - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredLinkAddonRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ConfiguredLinkAddonRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/ConfiguredLinkAddonRequest' - required: true - responses: - '201': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredLinkAddonResponse' - description: '' - tags: - - configured-link-addons - /v1/configured-link-addons/{id}/: - delete: - description: viewset allowing create, retrieve, update, delete - operationId: configured_link_addons_destroy - parameters: - - description: A UUID string identifying this Configured Link Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - responses: - '204': - description: No response body - tags: - - configured-link-addons - get: - description: viewset allowing create, retrieve, update, delete - operationId: configured_link_addons_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-link-addons] - schema: - items: - enum: - - id - - display_name - - target_url - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_link_service - - current_user_is_owner - - external_service_name - - resource_type - - target_id - type: string - type: array - - description: A UUID string identifying this Configured Link Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_link_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredLinkAddonResponse' - description: '' - tags: - - configured-link-addons - patch: - description: viewset allowing create, retrieve, update, delete - operationId: configured_link_addons_partial_update - parameters: - - description: A UUID string identifying this Configured Link Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PatchedConfiguredLinkAddonRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedConfiguredLinkAddonRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedConfiguredLinkAddonRequest' - required: true - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredLinkAddonResponse' - description: '' - tags: - - configured-link-addons - /v1/configured-link-addons/{id}/authorized_resource: - get: - description: Fetch all related ResourceReference to this ConfiguredLinkAddon - operationId: configured_link_addons_retrieve_2_related_authorized_resource - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[resource-references] - schema: - items: - enum: - - url - - resource_uri - - configured_storage_addons - - configured_link_addons - - configured_citation_addons - - configured_computing_addons - type: string - type: array - - description: A UUID string identifying this Resource Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - configured_storage_addons - - configured_citation_addons - - configured_link_addons - - configured_computing_addons - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ResourceReferenceResponse' - description: '' - tags: &id008 - - configured-link-addons - /v1/configured-link-addons/{id}/base_account: - get: - description: Fetch all related AuthorizedLinkAccount to this ConfiguredLinkAddon - operationId: configured_link_addons_retrieve_2_related_base_account - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-link-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_link_addons - - credentials - - external_link_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Link Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_link_service - - configured_link_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedLinkAccountResponse' - description: '' - tags: *id008 - /v1/configured-link-addons/{id}/connected_operations: - get: - description: Fetch all related AddonOperation to this ConfiguredLinkAddon - operationId: configured_link_addons_retrieve_2_related_connected_operations - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: *id008 - /v1/configured-link-addons/{id}/external_link_service: - get: - description: Fetch all related ExternalLinkService to this ConfiguredLinkAddon - operationId: configured_link_addons_retrieve_2_related_external_link_service - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-link-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - wb_key - - external_service_name - - configurable_api_root - - supported_resource_types - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Link Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalLinkServiceResponse' - description: '' - security: - - {} - tags: *id008 - /v1/configured-link-addons/{id}/verified-links/: - get: - description: viewset allowing create, retrieve, update, delete - operationId: configured_link_addons_verified_links_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-link-addons] - schema: - items: - enum: - - id - - display_name - - target_url - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_link_service - - current_user_is_owner - - external_service_name - - resource_type - - target_id - type: string - type: array - - description: A UUID string identifying this Configured Link Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_link_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredLinkAddonResponse' - description: '' - tags: - - configured-link-addons - /v1/configured-storage-addons/: - post: - description: "Create new configured storage addon for given authorized storage\ - \ account, linking it to desired project.\n To configure it properly, you\ - \ must specify `root_folder` on the provider's side.\n Note that everything\ - \ under this folder is going to be accessible to everyone who has access to\ - \ this project" - operationId: configured_storage_addons_create - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredStorageAddonRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ConfiguredStorageAddonRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/ConfiguredStorageAddonRequest' - required: true - responses: - '201': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredStorageAddonResponse' - description: '' - tags: - - configured-storage-addons - /v1/configured-storage-addons/{id}/: - delete: - description: viewset allowing create, retrieve, update, delete - operationId: configured_storage_addons_destroy - parameters: - - description: A UUID string identifying this Configured Storage Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - responses: - '204': - description: No response body - tags: - - configured-storage-addons - get: - description: viewset allowing create, retrieve, update, delete - operationId: configured_storage_addons_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-storage-addons] - schema: - items: - enum: - - id - - url - - display_name - - root_folder - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_storage_service - - current_user_is_owner - - external_service_name - type: string - type: array - - description: A UUID string identifying this Configured Storage Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_storage_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredStorageAddonResponse' - description: '' - tags: - - configured-storage-addons - patch: - description: viewset allowing create, retrieve, update, delete - operationId: configured_storage_addons_partial_update - parameters: - - description: A UUID string identifying this Configured Storage Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PatchedConfiguredStorageAddonRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedConfiguredStorageAddonRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedConfiguredStorageAddonRequest' - required: true - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredStorageAddonResponse' - description: '' - tags: - - configured-storage-addons - /v1/configured-storage-addons/{id}/authorized_resource: - get: - description: Fetch all related ResourceReference to this ConfiguredStorageAddon - operationId: configured_storage_addons_retrieve_2_related_authorized_resource - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[resource-references] - schema: - items: - enum: - - url - - resource_uri - - configured_storage_addons - - configured_link_addons - - configured_citation_addons - - configured_computing_addons - type: string - type: array - - description: A UUID string identifying this Resource Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - configured_storage_addons - - configured_citation_addons - - configured_link_addons - - configured_computing_addons - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ResourceReferenceResponse' - description: '' - tags: &id009 - - configured-storage-addons - /v1/configured-storage-addons/{id}/base_account: - get: - description: Fetch all related AuthorizedStorageAccount to this ConfiguredStorageAddon - operationId: configured_storage_addons_retrieve_2_related_base_account - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-storage-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_storage_addons - - credentials - - default_root_folder - - external_storage_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Storage Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_storage_service - - configured_storage_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedStorageAccountResponse' - description: '' - tags: *id009 - /v1/configured-storage-addons/{id}/connected_operations: - get: - description: Fetch all related AddonOperation to this ConfiguredStorageAddon - operationId: configured_storage_addons_retrieve_2_related_connected_operations - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-operations] - schema: - items: - enum: - - url - - name - - docstring - - capability - - kwargs_jsonschema - - result_jsonschema - - implemented_by - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_by - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonOperationResponse' - description: '' - security: - - {} - tags: *id009 - /v1/configured-storage-addons/{id}/external_storage_service: - get: - description: Fetch all related ExternalStorageService to this ConfiguredStorageAddon - operationId: configured_storage_addons_retrieve_2_related_external_storage_service - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-storage-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - max_concurrent_downloads - - max_upload_mb - - display_name - - url - - wb_key - - external_service_name - - configurable_api_root - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Storage Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalStorageServiceResponse' - description: '' - security: - - {} - tags: *id009 - /v1/external-citation-services/: - get: - description: Get the list of all available external citation services - operationId: external_citation_services_list - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-citation-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - configurable_api_root - - wb_key - - external_service_name - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - - description: A page number within the paginated result set. - in: query - name: page[number] - required: false - schema: - type: integer - - description: Number of results to return per page. - in: query - name: page[size] - required: false - schema: - type: integer - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PaginatedExternalCitationServiceList' - description: '' - security: - - {} - tags: - - external-citation-services - /v1/external-citation-services/{id}/: - get: - description: "This mixin provides a helper attributes to select or prefetch\ - \ related models\nbased on the include specified in the URL.\n\n__all__ can\ - \ be used to specify a prefetch which should be done regardless of the include\n\ - \n\n.. code:: python\n\n # When MyViewSet is called with ?include=author\ - \ it will prefetch author and authorbio\n class MyViewSet(viewsets.ModelViewSet):\n\ - \ queryset = Book.objects.all()\n prefetch_for_includes = {\n\ - \ '__all__': [],\n 'category.section': ['category']\n\ - \ }\n select_for_includes = {\n '__all__': [],\n\ - \ 'author': ['author', 'author__authorbio'],\n }" - operationId: external_citation_services_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-citation-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - configurable_api_root - - wb_key - - external_service_name - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Citation Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalCitationServiceResponse' - description: '' - security: - - {} - tags: - - external-citation-services - /v1/external-citation-services/{id}/addon_imp: - get: - description: Fetch all related AddonImp to this ExternalCitationService - operationId: external_citation_services_retrieve_2_related_addon_imp - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-imps] - schema: - items: - enum: - - url - - name - - docstring - - interface_docstring - - implemented_operations - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonImpResponse' - description: '' - security: - - {} - tags: - - external-citation-services - /v1/external-computing-services/: - get: - description: Get the list of all available external computing services - operationId: external_computing_services_list - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-computing-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - configurable_api_root - - supported_features - - icon_url - - wb_key - - api_base_url_options - type: string - type: array - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - - description: A page number within the paginated result set. - in: query - name: page[number] - required: false - schema: - type: integer - - description: Number of results to return per page. - in: query - name: page[size] - required: false - schema: - type: integer - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PaginatedExternalComputingServiceList' - description: '' - security: - - {} - tags: - - external-computing-services - /v1/external-computing-services/{id}/: - get: - description: "This mixin provides a helper attributes to select or prefetch\ - \ related models\nbased on the include specified in the URL.\n\n__all__ can\ - \ be used to specify a prefetch which should be done regardless of the include\n\ - \n\n.. code:: python\n\n # When MyViewSet is called with ?include=author\ - \ it will prefetch author and authorbio\n class MyViewSet(viewsets.ModelViewSet):\n\ - \ queryset = Book.objects.all()\n prefetch_for_includes = {\n\ - \ '__all__': [],\n 'category.section': ['category']\n\ - \ }\n select_for_includes = {\n '__all__': [],\n\ - \ 'author': ['author', 'author__authorbio'],\n }" - operationId: external_computing_services_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-computing-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - configurable_api_root - - supported_features - - icon_url - - wb_key - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Computing Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalComputingServiceResponse' - description: '' - security: - - {} - tags: - - external-computing-services - /v1/external-computing-services/{id}/addon_imp: - get: - description: Fetch all related AddonImp to this ExternalComputingService - operationId: external_computing_services_retrieve_2_related_addon_imp - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-imps] - schema: - items: - enum: - - url - - name - - docstring - - interface_docstring - - implemented_operations - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonImpResponse' - description: '' - security: - - {} - tags: - - external-computing-services - /v1/external-link-services/: - get: - description: Get the list of all available external link services - operationId: external_link_services_list - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-link-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - wb_key - - external_service_name - - configurable_api_root - - supported_resource_types - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - - description: A page number within the paginated result set. - in: query - name: page[number] - required: false - schema: - type: integer - - description: Number of results to return per page. - in: query - name: page[size] - required: false - schema: - type: integer - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PaginatedExternalLinkServiceList' - description: '' - security: - - {} - tags: - - external-link-services - /v1/external-link-services/{id}/: - get: - description: "This mixin provides a helper attributes to select or prefetch\ - \ related models\nbased on the include specified in the URL.\n\n__all__ can\ - \ be used to specify a prefetch which should be done regardless of the include\n\ - \n\n.. code:: python\n\n # When MyViewSet is called with ?include=author\ - \ it will prefetch author and authorbio\n class MyViewSet(viewsets.ModelViewSet):\n\ - \ queryset = Book.objects.all()\n prefetch_for_includes = {\n\ - \ '__all__': [],\n 'category.section': ['category']\n\ - \ }\n select_for_includes = {\n '__all__': [],\n\ - \ 'author': ['author', 'author__authorbio'],\n }" - operationId: external_link_services_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-link-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - display_name - - url - - wb_key - - external_service_name - - configurable_api_root - - supported_resource_types - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Link Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalLinkServiceResponse' - description: '' - security: - - {} - tags: - - external-link-services - /v1/external-link-services/{id}/addon_imp: - get: - description: Fetch all related AddonImp to this ExternalLinkService - operationId: external_link_services_retrieve_2_related_addon_imp - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-imps] - schema: - items: - enum: - - url - - name - - docstring - - interface_docstring - - implemented_operations - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonImpResponse' - description: '' - security: - - {} - tags: - - external-link-services - /v1/external-storage-services/: - get: - description: Get the list of all available external storage services - operationId: external_storage_services_list - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-storage-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - max_concurrent_downloads - - max_upload_mb - - display_name - - url - - wb_key - - external_service_name - - configurable_api_root - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - - description: A page number within the paginated result set. - in: query - name: page[number] - required: false - schema: - type: integer - - description: Number of results to return per page. - in: query - name: page[size] - required: false - schema: - type: integer - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PaginatedExternalStorageServiceList' - description: '' - security: - - {} - tags: - - external-storage-services - /v1/external-storage-services/{id}/: - get: - description: "This mixin provides a helper attributes to select or prefetch\ - \ related models\nbased on the include specified in the URL.\n\n__all__ can\ - \ be used to specify a prefetch which should be done regardless of the include\n\ - \n\n.. code:: python\n\n # When MyViewSet is called with ?include=author\ - \ it will prefetch author and authorbio\n class MyViewSet(viewsets.ModelViewSet):\n\ - \ queryset = Book.objects.all()\n prefetch_for_includes = {\n\ - \ '__all__': [],\n 'category.section': ['category']\n\ - \ }\n select_for_includes = {\n '__all__': [],\n\ - \ 'author': ['author', 'author__authorbio'],\n }" - operationId: external_storage_services_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[external-storage-services] - schema: - items: - enum: - - id - - addon_imp - - auth_uri - - credentials_format - - max_concurrent_downloads - - max_upload_mb - - display_name - - url - - wb_key - - external_service_name - - configurable_api_root - - supported_features - - icon_url - - api_base_url_options - type: string - type: array - - description: A UUID string identifying this External Storage Service. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - addon_imp - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ExternalStorageServiceResponse' - description: '' - security: - - {} - tags: - - external-storage-services - /v1/external-storage-services/{id}/addon_imp: - get: - description: Fetch all related AddonImp to this ExternalStorageService - operationId: external_storage_services_retrieve_2_related_addon_imp - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[addon-imps] - schema: - items: - enum: - - url - - name - - docstring - - interface_docstring - - implemented_operations - type: string - type: array - - in: path - name: id - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - implemented_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AddonImpResponse' - description: '' - security: - - {} - tags: - - external-storage-services - /v1/resource-references/: - get: - description: Get resource reference by resource_uri. Even through this is a - list method, this endpoint returns only one entity - operationId: resource_references_list - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[resource-references] - schema: - items: - enum: - - url - - resource_uri - - configured_storage_addons - - configured_link_addons - - configured_citation_addons - - configured_computing_addons - type: string - type: array - - description: Filter by resource_uri. This filter must be uniquely identifying. - in: query - name: filter[resource_uri] - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - configured_storage_addons - - configured_citation_addons - - configured_link_addons - - configured_computing_addons - type: string - type: array - - description: A page number within the paginated result set. - in: query - name: page[number] - required: false - schema: - type: integer - - description: Number of results to return per page. - in: query - name: page[size] - required: false - schema: - type: integer - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PaginatedResourceReferenceList' - description: '' - tags: - - resource-references - /v1/resource-references/{id}/: - get: - description: Get resource reference by it's pk - operationId: resource_references_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[resource-references] - schema: - items: - enum: - - url - - resource_uri - - configured_storage_addons - - configured_link_addons - - configured_citation_addons - - configured_computing_addons - type: string - type: array - - description: A UUID string identifying this Resource Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - configured_storage_addons - - configured_citation_addons - - configured_link_addons - - configured_computing_addons - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ResourceReferenceResponse' - description: '' - tags: - - resource-references - /v1/resource-references/{id}/configured_citation_addons: - get: - description: Fetch all related ConfiguredCitationAddon to this ResourceReference - operationId: resource_references_retrieve_2_related_configured_citation_addons - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-citation-addons] - schema: - items: - enum: - - id - - url - - display_name - - root_folder - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_service_name - - external_citation_service - - current_user_is_owner - type: string - type: array - - description: A UUID string identifying this Configured Citation Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_citation_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredCitationAddonResponse' - description: '' - tags: &id010 - - resource-references - /v1/resource-references/{id}/configured_computing_addons: - get: - description: Fetch all related ConfiguredComputingAddon to this ResourceReference - operationId: resource_references_retrieve_2_related_configured_computing_addons - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-computing-addons] - schema: - items: - enum: - - id - - url - - display_name - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_service_name - - external_computing_service - - current_user_is_owner - type: string - type: array - - description: A UUID string identifying this Configured Computing Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_computing_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredComputingAddonResponse' - description: '' - tags: *id010 - /v1/resource-references/{id}/configured_link_addons: - get: - description: Fetch all related ConfiguredLinkAddon to this ResourceReference - operationId: resource_references_retrieve_2_related_configured_link_addons - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-link-addons] - schema: - items: - enum: - - id - - display_name - - target_url - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_link_service - - current_user_is_owner - - external_service_name - - resource_type - - target_id - type: string - type: array - - description: A UUID string identifying this Configured Link Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_link_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredLinkAddonResponse' - description: '' - tags: *id010 - /v1/resource-references/{id}/configured_storage_addons: - get: - description: Fetch all related ConfiguredStorageAddon to this ResourceReference - operationId: resource_references_retrieve_2_related_configured_storage_addons - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[configured-storage-addons] - schema: - items: - enum: - - id - - url - - display_name - - root_folder - - base_account - - authorized_resource - - authorized_resource_uri - - connected_capabilities - - connected_operations - - connected_operation_names - - external_storage_service - - current_user_is_owner - - external_service_name - type: string - type: array - - description: A UUID string identifying this Configured Storage Addon. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - base_account - - external_storage_service - - authorized_resource - - connected_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ConfiguredStorageAddonResponse' - description: '' - tags: *id010 - /v1/user-references/: - get: - description: Get user reference by user_uri. Even through this is a list method, - this endpoint returns only one entity - operationId: user_references_list - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[user-references] - schema: - items: - enum: - - url - - user_uri - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - - description: Filter by user_uri. This filter must be uniquely identifying. - in: query - name: filter[user_uri] - required: true - schema: - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - - description: A page number within the paginated result set. - in: query - name: page[number] - required: false - schema: - type: integer - - description: Number of results to return per page. - in: query - name: page[size] - required: false - schema: - type: integer - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/PaginatedUserReferenceList' - description: '' - tags: - - user-references - /v1/user-references/{id}/: - get: - description: Get user reference by it's pk - operationId: user_references_retrieve - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[user-references] - schema: - items: - enum: - - url - - user_uri - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - - description: A UUID string identifying this User Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - authorized_storage_accounts - - authorized_citation_accounts - - authorized_computing_accounts - - authorized_link_accounts - - configured_resources - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/UserReferenceResponse' - description: '' - tags: - - user-references - /v1/user-references/{id}/authorized_citation_accounts: - get: - description: Fetch all related AuthorizedCitationAccount to this UserReference - operationId: user_references_retrieve_2_related_authorized_citation_accounts - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-citation-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_citation_addons - - credentials - - default_root_folder - - external_citation_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Citation Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_citation_service - - configured_citation_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedCitationAccountResponse' - description: '' - tags: &id011 - - user-references - /v1/user-references/{id}/authorized_computing_accounts: - get: - description: Fetch all related AuthorizedComputingAccount to this UserReference - operationId: user_references_retrieve_2_related_authorized_computing_accounts - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-computing-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_computing_addons - - credentials - - external_computing_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Computing Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_computing_service - - configured_computing_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedComputingAccountResponse' - description: '' - tags: *id011 - /v1/user-references/{id}/authorized_link_accounts: - get: - description: Fetch all related AuthorizedCitationAccount to this UserReference - operationId: user_references_retrieve_2_related_authorized_link_accounts - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-citation-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_citation_addons - - credentials - - default_root_folder - - external_citation_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Citation Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_citation_service - - configured_citation_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedCitationAccountResponse' - description: '' - tags: *id011 - /v1/user-references/{id}/authorized_storage_accounts: - get: - description: Fetch all related AuthorizedStorageAccount to this UserReference - operationId: user_references_retrieve_2_related_authorized_storage_accounts - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[authorized-storage-accounts] - schema: - items: - enum: - - id - - url - - display_name - - account_owner - - api_base_url - - auth_url - - authorized_capabilities - - authorized_operations - - authorized_operation_names - - configured_storage_addons - - credentials - - default_root_folder - - external_storage_service - - initiate_oauth - - credentials_available - type: string - type: array - - description: A UUID string identifying this Authorized Storage Account. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - account_owner - - external_storage_service - - configured_storage_addons - - authorized_operations - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/AuthorizedStorageAccountResponse' - description: '' - tags: *id011 - /v1/user-references/{id}/configured_resources: - get: - description: Fetch all related ResourceReference to this UserReference - operationId: user_references_retrieve_2_related_configured_resources - parameters: - - description: endpoint return only specific fields in the response on a per-type - basis by including a fields[TYPE] query parameter. - explode: false - in: query - name: fields[resource-references] - schema: - items: - enum: - - url - - resource_uri - - configured_storage_addons - - configured_link_addons - - configured_citation_addons - - configured_computing_addons - type: string - type: array - - description: A UUID string identifying this Resource Reference. - in: path - name: id - required: true - schema: - format: uuid - type: string - - description: include query parameter to allow the client to customize which - related resources should be returned. - explode: false - in: query - name: include - schema: - items: - enum: - - configured_storage_addons - - configured_citation_addons - - configured_link_addons - - configured_computing_addons - type: string - type: array - responses: - '200': - content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/ResourceReferenceResponse' - description: '' - tags: *id011 From 28e25bdbcf51d22f6117a896b223c6c76143d1a0 Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Tue, 1 Jul 2025 12:17:48 -0400 Subject: [PATCH 091/100] add SESSION_COOKIE_DOMIAN back --- app/env.py | 1 + app/settings.py | 1 + 2 files changed, 2 insertions(+) diff --git a/app/env.py b/app/env.py index c821233a..1523cacd 100644 --- a/app/env.py +++ b/app/env.py @@ -48,6 +48,7 @@ OSF_API_BASE_URL = os.environ.get("OSF_API_BASE_URL", "https://api.osf.example") OSF_AUTH_COOKIE_NAME = os.environ.get("OSF_AUTH_COOKIE_NAME", "osf_staging") OSF_AUTH_COOKIE_SECRET = os.environ.get("OSF_AUTH_COOKIE_SECRET", "CHANGEME") +SESSION_COOKIE_DOMAIN = os.environ.get("SESSION_COOKIE_DOMAIN") ### # amqp/celery diff --git a/app/settings.py b/app/settings.py index 814f2c82..497f3373 100644 --- a/app/settings.py +++ b/app/settings.py @@ -68,6 +68,7 @@ ALLOWED_RESOURCE_URI_PREFIXES = {OSF_BASE_URL} SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_COOKIE_NAME = env.OSF_AUTH_COOKIE_NAME +SESSION_COOKIE_DOMAIN = env.SESSION_COOKIE_DOMAIN OSF_AUTH_COOKIE_SECRET = env.OSF_AUTH_COOKIE_SECRET REDIS_HOST = env.REDIS_HOST From 79b436cd5a29539b8fd949b574657c379dc86da5 Mon Sep 17 00:00:00 2001 From: Yuhuai Liu Date: Wed, 2 Jul 2025 03:22:58 -0400 Subject: [PATCH 092/100] delete extra session middleware; modify process_response --- app/middleware.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++ app/settings.py | 1 - 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/app/middleware.py b/app/middleware.py index 927a868d..52e5e8c7 100644 --- a/app/middleware.py +++ b/app/middleware.py @@ -1,8 +1,13 @@ +import time from importlib import import_module import itsdangerous from django.conf import settings +from django.contrib.sessions.backends.base import UpdateError +from django.contrib.sessions.exceptions import SessionInterrupted from django.contrib.sessions.middleware import SessionMiddleware +from django.utils.cache import patch_vary_headers +from django.utils.http import http_date SessionStore = import_module(settings.SESSION_ENGINE).SessionStore @@ -33,3 +38,64 @@ def process_request(self, request): request.session = SessionStore(session_key=session_key) else: request.session = SessionStore() + + def process_response(self, request, response): + """ + If request.session was modified, or if the configuration is to save the + session every time, save the changes and set a session cookie or delete + the session cookie if the session has been emptied. + This is mostly a direct port from django SessionMiddleware.process_response + The only difference is that the cookie value we set is also signed. + """ + try: + accessed = request.session.accessed + modified = request.session.modified + empty = request.session.is_empty() + except AttributeError: + return response + # First check if we need to delete this cookie. + # The session should be deleted only if the session is entirely empty. + if settings.SESSION_COOKIE_NAME in request.COOKIES and empty: + response.delete_cookie( + settings.SESSION_COOKIE_NAME, + path=settings.SESSION_COOKIE_PATH, + domain=settings.SESSION_COOKIE_DOMAIN, + samesite=settings.SESSION_COOKIE_SAMESITE, + ) + patch_vary_headers(response, ("Cookie",)) + else: + if accessed: + patch_vary_headers(response, ("Cookie",)) + if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: + if request.session.get_expire_at_browser_close(): + max_age = None + expires = None + else: + max_age = request.session.get_expiry_age() + expires_time = time.time() + max_age + expires = http_date(expires_time) + if response.status_code < 500: + try: + request.session.save() + except UpdateError: + raise SessionInterrupted( + "The request's session was deleted before the " + "request completed. The user may have logged " + "out in a concurrent request, for example." + ) + response.set_cookie( + settings.SESSION_COOKIE_NAME, + ensure_str( + itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET).sign( + request.session.session_key + ) + ), + max_age=max_age, + expires=expires, + domain=settings.SESSION_COOKIE_DOMAIN, + path=settings.SESSION_COOKIE_PATH, + secure=settings.SESSION_COOKIE_SECURE or None, + httponly=settings.SESSION_COOKIE_HTTPONLY or None, + samesite=settings.SESSION_COOKIE_SAMESITE, + ) + return response diff --git a/app/settings.py b/app/settings.py index 497f3373..2ac644d1 100644 --- a/app/settings.py +++ b/app/settings.py @@ -115,7 +115,6 @@ "django.middleware.security.SecurityMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", "app.middleware.UnsignCookieSessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", From 3311a29eab13e5c0a56ef8405dd72dea81281b8a Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 2 Jul 2025 16:51:57 +0300 Subject: [PATCH 093/100] pr fixes --- .../addon_operation_invocation/views.py | 4 -- .../authorized_account/citation/views.py | 4 +- .../authorized_account/computing/views.py | 4 +- .../authorized_account/link/views.py | 4 +- .../authorized_account/storage/views.py | 4 +- addon_service/common/schemas.py | 47 ------------------- addon_service/oauth1/views.py | 1 + addon_service/oauth2/views.py | 1 + 8 files changed, 14 insertions(+), 55 deletions(-) delete mode 100644 addon_service/common/schemas.py diff --git a/addon_service/addon_operation_invocation/views.py b/addon_service/addon_operation_invocation/views.py index 2faaae8d..300a4bb5 100644 --- a/addon_service/addon_operation_invocation/views.py +++ b/addon_service/addon_operation_invocation/views.py @@ -6,7 +6,6 @@ from addon_service.common.permissions import ( IsAuthenticated, - SessionUserIsOwner, SessionUserMayAccessInvocation, SessionUserMayPerformInvocation, ) @@ -52,9 +51,6 @@ def get_permissions(self): match self.action: case "retrieve" | "retrieve_related": return [IsAuthenticated(), SessionUserMayAccessInvocation()] - case "partial_update" | "update" | "destroy": - # prohibit this? Maybe allow only to delete invocation from action log, but definitely not update - return [IsAuthenticated(), SessionUserIsOwner()] case "create": return [SessionUserMayPerformInvocation()] case None: diff --git a/addon_service/authorized_account/citation/views.py b/addon_service/authorized_account/citation/views.py index 452b9b1b..c9671861 100644 --- a/addon_service/authorized_account/citation/views.py +++ b/addon_service/authorized_account/citation/views.py @@ -11,7 +11,9 @@ @extend_schema_view( create=extend_schema( - description='Create new authorized citation account for given external citation service.\n For OAuth services it\'s required to create account with `"initiate_oauth"=true` in order to proceed with OAuth flow' + description="Create new authorized citation account for given external citation service. " + 'For OAuth services it\'s required to create account with `"initiate_oauth"=true` ' + "in order to proceed with OAuth flow" ), ) class AuthorizedCitationAccountViewSet(AuthorizedAccountViewSet): diff --git a/addon_service/authorized_account/computing/views.py b/addon_service/authorized_account/computing/views.py index d986cd2f..fbadb0c9 100644 --- a/addon_service/authorized_account/computing/views.py +++ b/addon_service/authorized_account/computing/views.py @@ -11,7 +11,9 @@ @extend_schema_view( create=extend_schema( - description='Create new authorized computing account for given external computing service.\n For OAuth services it\'s required to create account with `"initiate_oauth"=true` in order to proceed with OAuth flow' + description="Create new authorized computing account for given external computing service.\n " + 'For OAuth services it\'s required to create account with `"initiate_oauth"=true` ' + "in order to proceed with OAuth flow" ), ) class AuthorizedComputingAccountViewSet(AuthorizedAccountViewSet): diff --git a/addon_service/authorized_account/link/views.py b/addon_service/authorized_account/link/views.py index 3f0440cb..c943ab06 100644 --- a/addon_service/authorized_account/link/views.py +++ b/addon_service/authorized_account/link/views.py @@ -11,7 +11,9 @@ @extend_schema_view( create=extend_schema( - description='Create new authorized link account for given external link service.\n For OAuth services it\'s required to create account with `"initiate_oauth"=true` in order to proceed with OAuth flow' + description="Create new authorized link account for given external link service.\n " + 'For OAuth services it\'s required to create account with `"initiate_oauth"=true` ' + "in order to proceed with OAuth flow" ), ) class AuthorizedLinkAccountViewSet(AuthorizedAccountViewSet): diff --git a/addon_service/authorized_account/storage/views.py b/addon_service/authorized_account/storage/views.py index fb66fe73..9c058047 100644 --- a/addon_service/authorized_account/storage/views.py +++ b/addon_service/authorized_account/storage/views.py @@ -15,7 +15,9 @@ @extend_schema_view( create=extend_schema( - description='Create new authorized storage account for given external storage service.\n For OAuth services it\'s required to create account with `"initiate_oauth"=true` in order to proceed with OAuth flow' + description="Create new authorized storage account for given external storage service." + '\n For OAuth services it\'s required to create account with `"initiate_oauth"=true` ' + "in order to proceed with OAuth flow" ), ) class AuthorizedStorageAccountViewSet(AuthorizedAccountViewSet): diff --git a/addon_service/common/schemas.py b/addon_service/common/schemas.py deleted file mode 100644 index 4d7ab6d7..00000000 --- a/addon_service/common/schemas.py +++ /dev/null @@ -1,47 +0,0 @@ -from rest_framework.decorators import action -from rest_framework_json_api.relations import ResourceRelatedField - - -def auto_related_actions(cls): - """ - A class decorator that automatically adds a DRF @action for each - relationship on a ViewSet's serializer_class. - """ - # Find the serializer - serializer_class = getattr(cls, "serializer_class", None) - if not serializer_class: - return cls - - # Find all relationship fields - relationship_fields = { - field_name: field - for field_name, field in serializer_class().get_fields().items() - if isinstance(field, ResourceRelatedField) - } - - for field_name, field in relationship_fields.items(): - # This is the handler that will be used for our action. - # It's a bridge to the existing `retrieve_related` method. - def related_field_handler(self, request, *args, **kwargs): - # We pass the field_name explicitly to retrieve_related - kwargs["related_field"] = field_name - return self.retrieve_related(request, *args, **kwargs) - - # Set docstrings for better schema descriptions - related_field_handler.__doc__ = ( - f"Retrieve the related {field_name} for this resource." - ) - related_field_handler.__name__ = f"{field_name}_related_action" - - # Decorate our handler with @action. This is what the router looks for. - # The `url_path` will be the same as the field name. - decorated_handler = action( - detail=True, - methods=["get"], - url_path=field_name, - )(related_field_handler) - - # Attach the brand new, decorated method to the ViewSet class - setattr(cls, f"{field_name}_related_action", decorated_handler) - - return cls diff --git a/addon_service/oauth1/views.py b/addon_service/oauth1/views.py index 423338d4..227b25d9 100644 --- a/addon_service/oauth1/views.py +++ b/addon_service/oauth1/views.py @@ -11,6 +11,7 @@ from addon_service.osf_models.fields import decrypt_string +# Exclude oAuth views from openapi schema as they are from internal use only @extend_schema(exclude=True) def oauth1_callback_view(request): oauth_token = request.GET["oauth_token"] diff --git a/addon_service/oauth2/views.py b/addon_service/oauth2/views.py index 8dc6623d..4c6d30ff 100644 --- a/addon_service/oauth2/views.py +++ b/addon_service/oauth2/views.py @@ -13,6 +13,7 @@ from addon_service.oauth2.utils import get_initial_access_token +# Exclude oAuth views from openapi schema as they are from internal use only @extend_schema(exclude=True) @transaction.non_atomic_requests # async views and ATOMIC_REQUESTS do not mix async def oauth2_callback_view(request): From 423e06896faaf5b9716ecc84a625f102c8419d74 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 2 Jul 2025 12:47:35 -0400 Subject: [PATCH 094/100] Add default session/cookie setttings --- app/settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/settings.py b/app/settings.py index 2ac644d1..75a99094 100644 --- a/app/settings.py +++ b/app/settings.py @@ -69,6 +69,9 @@ SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_COOKIE_NAME = env.OSF_AUTH_COOKIE_NAME SESSION_COOKIE_DOMAIN = env.SESSION_COOKIE_DOMAIN +SESSION_COOKIE_SECURE = True +SESSION_COOKIE_HTTPONLY = True +SESSION_COOKIE_SAMESITE = "None" OSF_AUTH_COOKIE_SECRET = env.OSF_AUTH_COOKIE_SECRET REDIS_HOST = env.REDIS_HOST From a77a22f9108f3396d48dae456af9babefc75abd3 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 2 Jul 2025 15:49:05 -0400 Subject: [PATCH 095/100] Further customization of `process_response` --- app/middleware.py | 88 +++++++++++++++++++---------------------------- app/settings.py | 2 +- 2 files changed, 36 insertions(+), 54 deletions(-) diff --git a/app/middleware.py b/app/middleware.py index 52e5e8c7..5d5245bc 100644 --- a/app/middleware.py +++ b/app/middleware.py @@ -1,4 +1,3 @@ -import time from importlib import import_module import itsdangerous @@ -7,7 +6,6 @@ from django.contrib.sessions.exceptions import SessionInterrupted from django.contrib.sessions.middleware import SessionMiddleware from django.utils.cache import patch_vary_headers -from django.utils.http import http_date SessionStore = import_module(settings.SESSION_ENGINE).SessionStore @@ -21,9 +19,8 @@ def ensure_str(value): class UnsignCookieSessionMiddleware(SessionMiddleware): """ - Overrides the process_request hook of SessionMiddleware - to retrieve the session key for finding the correct session - by unsigning the cookie value using server secret + Overrides the process_request hook of SessionMiddleware to retrieve the session key for finding/setting the + correct session by unsigning/signing the cookie value using server secret. """ def process_request(self, request): @@ -41,11 +38,11 @@ def process_request(self, request): def process_response(self, request, response): """ - If request.session was modified, or if the configuration is to save the - session every time, save the changes and set a session cookie or delete - the session cookie if the session has been emptied. - This is mostly a direct port from django SessionMiddleware.process_response - The only difference is that the cookie value we set is also signed. + If `request.session` was modified, or if the configuration is to save the session every time, save the changes + and set a session cookie. This is port from `SessionMiddleware.process_response` with the following changes: + 1) Sign cookie value using server secret. + 2) Don't delete cookie. + 3) Don't set `Max-Age` or `Expires` """ try: accessed = request.session.accessed @@ -53,49 +50,34 @@ def process_response(self, request, response): empty = request.session.is_empty() except AttributeError: return response - # First check if we need to delete this cookie. - # The session should be deleted only if the session is entirely empty. - if settings.SESSION_COOKIE_NAME in request.COOKIES and empty: - response.delete_cookie( - settings.SESSION_COOKIE_NAME, - path=settings.SESSION_COOKIE_PATH, - domain=settings.SESSION_COOKIE_DOMAIN, - samesite=settings.SESSION_COOKIE_SAMESITE, - ) + + if accessed: patch_vary_headers(response, ("Cookie",)) - else: - if accessed: - patch_vary_headers(response, ("Cookie",)) - if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: - if request.session.get_expire_at_browser_close(): - max_age = None - expires = None - else: - max_age = request.session.get_expiry_age() - expires_time = time.time() + max_age - expires = http_date(expires_time) - if response.status_code < 500: - try: - request.session.save() - except UpdateError: - raise SessionInterrupted( - "The request's session was deleted before the " - "request completed. The user may have logged " - "out in a concurrent request, for example." - ) - response.set_cookie( - settings.SESSION_COOKIE_NAME, - ensure_str( - itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET).sign( - request.session.session_key - ) - ), - max_age=max_age, - expires=expires, - domain=settings.SESSION_COOKIE_DOMAIN, - path=settings.SESSION_COOKIE_PATH, - secure=settings.SESSION_COOKIE_SECURE or None, - httponly=settings.SESSION_COOKIE_HTTPONLY or None, - samesite=settings.SESSION_COOKIE_SAMESITE, + + # GV only accesses or modifies OSF cookie, but does not delete it. OSF handles the creation and deletion. + if settings.SESSION_COOKIE_NAME in request.COOKIES and empty: + return response + + if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: + if response.status_code < 500: + try: + request.session.save() + except UpdateError: + raise SessionInterrupted( + "The request's session was deleted before the request completed. " + "The user may have logged out in a concurrent request, for example." ) + response.set_cookie( + settings.SESSION_COOKIE_NAME, + ensure_str( + itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET).sign( + request.session.session_key + ) + ), + domain=settings.SESSION_COOKIE_DOMAIN, + secure=settings.SESSION_COOKIE_SECURE, + httponly=settings.SESSION_COOKIE_HTTPONLY, + samesite=settings.SESSION_COOKIE_SAMESITE, + ) + return response diff --git a/app/settings.py b/app/settings.py index 75a99094..2e70c329 100644 --- a/app/settings.py +++ b/app/settings.py @@ -71,7 +71,7 @@ SESSION_COOKIE_DOMAIN = env.SESSION_COOKIE_DOMAIN SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True -SESSION_COOKIE_SAMESITE = "None" +SESSION_COOKIE_SAMESITE = None OSF_AUTH_COOKIE_SECRET = env.OSF_AUTH_COOKIE_SECRET REDIS_HOST = env.REDIS_HOST From 02a3d12646e5110ca1c1760c4c0f38b14dd19400 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Thu, 3 Jul 2025 15:50:05 -0400 Subject: [PATCH 096/100] Fix session cookie samesite --- app/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/settings.py b/app/settings.py index 2e70c329..75a99094 100644 --- a/app/settings.py +++ b/app/settings.py @@ -71,7 +71,7 @@ SESSION_COOKIE_DOMAIN = env.SESSION_COOKIE_DOMAIN SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True -SESSION_COOKIE_SAMESITE = None +SESSION_COOKIE_SAMESITE = "None" OSF_AUTH_COOKIE_SECRET = env.OSF_AUTH_COOKIE_SECRET REDIS_HOST = env.REDIS_HOST From 3ca2e32d1f6fc744e0006c3c4bbca15b1a219849 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 4 Jul 2025 14:19:51 +0300 Subject: [PATCH 097/100] fixed tests --- .../test_by_type/test_authorized_link_account.py | 10 ++++++++-- .../tests/test_by_type/test_configured_link_addon.py | 10 ++++++++-- .../test_configured_link_addon_null_values.py | 6 +++++- .../test_by_type/test_configured_storage_addon.py | 9 +++++++-- .../test_by_type/test_enhanced_resource_types.py | 6 +++++- .../test_by_type/test_null_resource_link_addon.py | 6 +++++- .../tests/test_by_type/test_session_sharing.py | 12 ++++++------ app/middleware.py | 1 + 8 files changed, 45 insertions(+), 15 deletions(-) diff --git a/addon_service/tests/test_by_type/test_authorized_link_account.py b/addon_service/tests/test_by_type/test_authorized_link_account.py index 5530a1bb..328a2628 100644 --- a/addon_service/tests/test_by_type/test_authorized_link_account.py +++ b/addon_service/tests/test_by_type/test_authorized_link_account.py @@ -6,6 +6,7 @@ from django.test import TestCase from django.urls import reverse +from itsdangerous import Signer from rest_framework.test import APITestCase from addon_service import models as db @@ -24,6 +25,7 @@ AccessTokenCredentials, UsernamePasswordCredentials, ) +from app import settings MOCK_CREDENTIALS = { @@ -101,7 +103,9 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies["osf"] = self._user.user_uri + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = ( + Signer(settings.OSF_AUTH_COOKIE_SECRET).sign(self._user.user_uri).decode() + ) self._mock_osf = MockOSF() self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) @@ -343,7 +347,9 @@ def setUpTestData(cls): cls._external_service = _factories.ExternalLinkOAuth2ServiceFactory() def setUp(self): - self.client.cookies["osf"] = self._user.user_uri + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = ( + Signer(settings.OSF_AUTH_COOKIE_SECRET).sign(self._user.user_uri).decode() + ) self._mock_osf = MockOSF() self._mock_osf.configure_assumed_caller(self._user.user_uri) diff --git a/addon_service/tests/test_by_type/test_configured_link_addon.py b/addon_service/tests/test_by_type/test_configured_link_addon.py index 48e5ab31..9ba91fd2 100644 --- a/addon_service/tests/test_by_type/test_configured_link_addon.py +++ b/addon_service/tests/test_by_type/test_configured_link_addon.py @@ -7,6 +7,7 @@ from django.core.exceptions import ValidationError from django.test import TestCase from django.urls import reverse +from itsdangerous import Signer from rest_framework.test import APITestCase from addon_service import models as db @@ -19,6 +20,7 @@ ) from addon_toolkit import AddonCapabilities from addon_toolkit.interfaces.link import SupportedResourceTypes +from app import settings def mock_target_url(self): @@ -49,7 +51,9 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies["osf"] = self._user.user_uri + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = ( + Signer(settings.OSF_AUTH_COOKIE_SECRET).sign(self._user.user_uri).decode() + ) self._mock_osf = MockOSF() self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" @@ -255,7 +259,9 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies["osf"] = self._user.user_uri + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = ( + Signer(settings.OSF_AUTH_COOKIE_SECRET).sign(self._user.user_uri).decode() + ) self._mock_osf = MockOSF() self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" diff --git a/addon_service/tests/test_by_type/test_configured_link_addon_null_values.py b/addon_service/tests/test_by_type/test_configured_link_addon_null_values.py index 3af55fab..e6e49ced 100644 --- a/addon_service/tests/test_by_type/test_configured_link_addon_null_values.py +++ b/addon_service/tests/test_by_type/test_configured_link_addon_null_values.py @@ -4,6 +4,7 @@ from django.test import TestCase from django.urls import reverse +from itsdangerous import Signer from rest_framework.test import APITestCase from addon_service import models as db @@ -14,6 +15,7 @@ from addon_service.tests._helpers import MockOSF from addon_toolkit import AddonCapabilities from addon_toolkit.interfaces.link import SupportedResourceTypes +from app import settings def mock_target_url(self): @@ -68,7 +70,9 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies["osf"] = self._user.user_uri + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = ( + Signer(settings.OSF_AUTH_COOKIE_SECRET).sign(self._user.user_uri).decode() + ) self._mock_osf = MockOSF() self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" diff --git a/addon_service/tests/test_by_type/test_configured_storage_addon.py b/addon_service/tests/test_by_type/test_configured_storage_addon.py index 115c97b2..efd8b27d 100644 --- a/addon_service/tests/test_by_type/test_configured_storage_addon.py +++ b/addon_service/tests/test_by_type/test_configured_storage_addon.py @@ -5,6 +5,7 @@ from django.test import TestCase from django.urls import reverse from django.utils import timezone +from itsdangerous import Signer from rest_framework.test import APITestCase from addon_service.common import hmac as hmac_utils @@ -26,7 +27,9 @@ def set_auth_header(self, auth_type): credentials = base64.b64encode(b"admin:password").decode() self.client.credentials(HTTP_AUTHORIZATION=f"Basic {credentials}") elif auth_type == "session": - self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = "some auth" + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = ( + Signer(settings.OSF_AUTH_COOKIE_SECRET).sign("some auth").decode() + ) elif auth_type == "no_auth": self.client.cookies.clear() self.client.credentials() @@ -210,7 +213,9 @@ def test_get_waterbutler_credentials(self): def test_get_waterbutler_credentials__error__no_headers(self): # credentials request requires HMAC-signed headers # Cookie + OSF-side permissions will not suffice - self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = "some auth" + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = ( + Signer(settings.OSF_AUTH_COOKIE_SECRET).sign("some_auth").decode() + ) _mock_osf = MockOSF() _mock_osf.configure_user_role( self._user.user_uri, self._configured_storage_addon.resource_uri, "admin" diff --git a/addon_service/tests/test_by_type/test_enhanced_resource_types.py b/addon_service/tests/test_by_type/test_enhanced_resource_types.py index dac2bb99..1725b13c 100644 --- a/addon_service/tests/test_by_type/test_enhanced_resource_types.py +++ b/addon_service/tests/test_by_type/test_enhanced_resource_types.py @@ -1,6 +1,7 @@ from http import HTTPStatus from django.test import TestCase +from itsdangerous import Signer from rest_framework.test import APITestCase from addon_service.common.credentials_formats import CredentialsFormats @@ -8,6 +9,7 @@ from addon_service.tests import _factories from addon_service.tests._helpers import MockOSF from addon_toolkit.interfaces.link import SupportedResourceTypes +from app import settings class TestResourceTypesSorting(APITestCase): @@ -24,7 +26,9 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies["osf"] = self._user.user_uri + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = ( + Signer(settings.OSF_AUTH_COOKIE_SECRET).sign(self._user.user_uri).decode() + ) self._mock_osf = MockOSF() self._mock_osf.configure_assumed_caller(self._user.user_uri) self.enterContext(self._mock_osf.mocking()) diff --git a/addon_service/tests/test_by_type/test_null_resource_link_addon.py b/addon_service/tests/test_by_type/test_null_resource_link_addon.py index b29e4ba5..2f851152 100644 --- a/addon_service/tests/test_by_type/test_null_resource_link_addon.py +++ b/addon_service/tests/test_by_type/test_null_resource_link_addon.py @@ -5,6 +5,7 @@ from django.core.exceptions import ValidationError from django.test import TestCase from django.urls import reverse +from itsdangerous import Signer from rest_framework.test import APITestCase from addon_service.configured_addon.link.models import is_supported_resource_type @@ -12,6 +13,7 @@ from addon_service.tests import _factories from addon_service.tests._helpers import MockOSF from addon_toolkit.interfaces.link import SupportedResourceTypes +from app import settings def mock_target_url(self): @@ -238,7 +240,9 @@ def setUpTestData(cls): def setUp(self): super().setUp() - self.client.cookies["osf"] = self._user.user_uri + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = ( + Signer(settings.OSF_AUTH_COOKIE_SECRET).sign(self._user.user_uri).decode() + ) self._mock_osf = MockOSF() self._mock_osf.configure_user_role( self._user.user_uri, self._resource.resource_uri, "admin" diff --git a/addon_service/tests/test_by_type/test_session_sharing.py b/addon_service/tests/test_by_type/test_session_sharing.py index af94d0e4..172995b6 100644 --- a/addon_service/tests/test_by_type/test_session_sharing.py +++ b/addon_service/tests/test_by_type/test_session_sharing.py @@ -30,7 +30,7 @@ def test_process_request_with_valid_cookie(self): signed_cookie = signer.sign(session_key) request = self.factory.get("/") - request.COOKIES = {settings.SESSION_COOKIE_NAME: signed_cookie} + request.COOKIES = {settings.SESSION_COOKIE_NAME: signed_cookie.decode()} result = self.middleware.process_request(request) @@ -73,7 +73,7 @@ def test_session_store_instantiation(self, mock_session_store): signed_cookie = signer.sign(session_key) request = self.factory.get("/") - request.COOKIES = {settings.SESSION_COOKIE_NAME: signed_cookie} + request.COOKIES = {settings.SESSION_COOKIE_NAME: signed_cookie.decode()} self.middleware.process_request(request) @@ -119,7 +119,7 @@ def test_api_access_with_shared_session(self): signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) signed_cookie = signer.sign(session_key) - self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = signed_cookie + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = signed_cookie.decode() session_store = SessionStore(session_key=session_key) session_store["user_reference_uri"] = self._user.user_uri @@ -135,7 +135,7 @@ def test_session_persistence_across_requests(self): signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) signed_cookie = signer.sign(session_key) - self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = signed_cookie + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = signed_cookie.decode() session_store = SessionStore(session_key=session_key) session_store["user_reference_uri"] = self._user.user_uri @@ -160,7 +160,7 @@ def test_invalid_session_handling(self): signer = itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET) signed_cookie = signer.sign(invalid_session_key) - self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = signed_cookie + self.client.cookies[settings.OSF_AUTH_COOKIE_NAME] = signed_cookie.decode() url = f"/v1/resource-references/{self._resource.pk}/" response = self.client.get(url) @@ -213,7 +213,7 @@ def test_session_key_encoding(self, mock_ensure_str): signed_cookie = signer.sign(session_key) request = RequestFactory().get("/") - request.COOKIES = {settings.SESSION_COOKIE_NAME: signed_cookie} + request.COOKIES = {settings.SESSION_COOKIE_NAME: signed_cookie.decode()} middleware = UnsignCookieSessionMiddleware(Mock()) middleware.process_request(request) diff --git a/app/middleware.py b/app/middleware.py index 5d5245bc..9ef2b0cb 100644 --- a/app/middleware.py +++ b/app/middleware.py @@ -31,6 +31,7 @@ def process_request(self, request): itsdangerous.Signer(settings.OSF_AUTH_COOKIE_SECRET).unsign(cookie) ) except itsdangerous.BadSignature: + request.session = SessionStore return None request.session = SessionStore(session_key=session_key) else: From 0eeea730437bfc6ddc22425bf8e21a02daed32f2 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Fri, 4 Jul 2025 09:53:15 -0400 Subject: [PATCH 098/100] Update session/cookie settings to come from env --- app/env.py | 11 +++++++++-- app/settings.py | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/env.py b/app/env.py index 1523cacd..a645f93f 100644 --- a/app/env.py +++ b/app/env.py @@ -46,9 +46,16 @@ OSF_HMAC_EXPIRATION_SECONDS = int(os.environ.get("OSF_HMAC_EXPIRATION_SECONDS", 110)) OSF_BASE_URL = os.environ.get("OSF_BASE_URL", "https://osf.example") OSF_API_BASE_URL = os.environ.get("OSF_API_BASE_URL", "https://api.osf.example") -OSF_AUTH_COOKIE_NAME = os.environ.get("OSF_AUTH_COOKIE_NAME", "osf_staging") +OSF_AUTH_COOKIE_NAME = os.environ.get("OSF_AUTH_COOKIE_NAME", "osf") OSF_AUTH_COOKIE_SECRET = os.environ.get("OSF_AUTH_COOKIE_SECRET", "CHANGEME") -SESSION_COOKIE_DOMAIN = os.environ.get("SESSION_COOKIE_DOMAIN") +SESSION_COOKIE_DOMAIN = os.environ.get("SESSION_COOKIE_DOMAIN", None) +SESSION_COOKIE_SECURE = os.environ.get( + "SESSION_COOKIE_SECURE", True +) # Change to False for local dev +SESSION_COOKIE_HTTPONLY = os.environ.get("SESSION_COOKIE_HTTPONLY", True) +SESSION_COOKIE_SAMESITE = os.environ.get( + "SESSION_COOKIE_SAMESITE", "None" +) # Change to "Lax" for local dev ### # amqp/celery diff --git a/app/settings.py b/app/settings.py index 75a99094..0f361035 100644 --- a/app/settings.py +++ b/app/settings.py @@ -69,9 +69,9 @@ SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_COOKIE_NAME = env.OSF_AUTH_COOKIE_NAME SESSION_COOKIE_DOMAIN = env.SESSION_COOKIE_DOMAIN -SESSION_COOKIE_SECURE = True -SESSION_COOKIE_HTTPONLY = True -SESSION_COOKIE_SAMESITE = "None" +SESSION_COOKIE_SECURE = env.SESSION_COOKIE_SECURE +SESSION_COOKIE_HTTPONLY = env.SESSION_COOKIE_HTTPONLY +SESSION_COOKIE_SAMESITE = env.SESSION_COOKIE_SAMESITE OSF_AUTH_COOKIE_SECRET = env.OSF_AUTH_COOKIE_SECRET REDIS_HOST = env.REDIS_HOST From fd7093519fbf74d53a16f2c0a4fba47ce2169483 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Fri, 4 Jul 2025 09:57:00 -0400 Subject: [PATCH 099/100] Update .gitignore --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d8c5b177..b0d66f2f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,9 @@ db.sqlite3 __pycache__ .venv - +.idea/ +.DS_Store +.DS_Store? +*.swp +*~ addon_service/static/gravyvalet_code_docs/ From a6ae60ca9f33d0eaea97088d031aa2beaa40379e Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 7 Jul 2025 15:48:57 +0300 Subject: [PATCH 100/100] fixed filling link dataverse --- addon_service/management/commands/providers/providers.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon_service/management/commands/providers/providers.csv b/addon_service/management/commands/providers/providers.csv index 62594c38..d986e48f 100644 --- a/addon_service/management/commands/providers/providers.csv +++ b/addon_service/management/commands/providers/providers.csv @@ -13,4 +13,4 @@ b670cf7a-752f-49ed-8367-8238cf8a24d1,Figshare,1,1,1007,{all},https://api.figshar 2acd3e09-5e27-4e5b-a3d1-579f16663cfc,Bitbucket,1,1,1012,{account repository team},https://api.bitbucket.org/2.0/,bitbucket,,1b2d71cc-425e-4aa6-886c-e2ff247c4f2c,bitbucket.svg,,10,10,511,,,storage 102c6356-e31a-4316-a9fe-8b2bcb20fdd6,Google Drive,1,1,1005,{https://www.googleapis.com/auth/drive},https://www.googleapis.com/,googledrive,,5cf93c06-c27f-4446-b8ac-ffac209ba465,google_drive.svg,,10,10,511,,,storage 5bc440c8-a03a-4aa6-bd9e-856aee9883e9,Owncloud,3,2,1009,{},,owncloud,,,owncloud.svg,,10,10,511,,,storage -edf57d0c-ae0a-4c85-b759-017fc8b009f0,Link dataverse,6,2,1030,{},https://demo.dataverse.org,,,,dataverse.svg,{},link,,,4294967295,,link +edf57d0c-ae0a-4c85-b759-017fc8b009f0,Link dataverse,6,2,1030,{},https://demo.dataverse.org,,,,dataverse.svg,{},,,104,4294967295,,link