Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Swagger move #283

Merged
merged 7 commits into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions biocompute/apis.py
Original file line number Diff line number Diff line change
@@ -1 +1,111 @@
#!/usr/bin/env python3
#biocompute/apis.py

"""BioCompute Object APIs
"""

from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from tests.fixtures.example_bco import BCO_000001
from config.services import legacy_api_converter
from biocompute.services import BcoDraftSerializer

class DraftsCreateApi(APIView):
"""
Create BCO Draft

--------------------

Creates a new BCO draft object.
"""

request_body = openapi.Schema(
type=openapi.TYPE_ARRAY,
title="Create BCO Draft Schema",
items=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=["prefix", "contents"],
properties={
"object_id": openapi.Schema(
type=openapi.TYPE_STRING,
description="BCO Object ID.",
example="https://biocomputeobject.org/TEST_000001"
),
"prefix": openapi.Schema(
type=openapi.TYPE_STRING,
description="BCO Prefix to use",
example="BCO"
),
"authorized_users": openapi.Schema(
type=openapi.TYPE_ARRAY,
description="Users which can access the BCO draft.",
items=openapi.Schema(type=openapi.TYPE_STRING, example="None")
),
"authorized_groups": openapi.Schema(
type=openapi.TYPE_ARRAY,
description="Group which can access the BCO draft.",
items=openapi.Schema(type=openapi.TYPE_STRING, example="None")
),
"contents": openapi.Schema(
type=openapi.TYPE_OBJECT,
description="Contents of the BCO.",
example=BCO_000001
),
},
),
description="BCO Drafts to create.",
)

@swagger_auto_schema(
request_body=request_body,
responses={
200: "Creation of BCO draft is successful.",
300: "Some requests failed and some succeeded.",
400: "Bad request.",
403: "Invalid token.",
},
tags=["BCO Management"],
)

def post(self, request) -> Response:
response_data = {}
owner = request.user
data = request.data
all_good = True
if 'POST_api_objects_draft_create' in request.data:
data = legacy_api_converter(request.data)

for index, object in enumerate(data):
list_id = object.get("object_id", index)
bco = BcoDraftSerializer(data=object, context={'request': request})

if bco.is_valid():
bco.create(bco.validated_data)
response_data[list_id] = "bco valid"

else:
response_data[list_id] = bco.errors
all_good = False

if all_good is False:
return Response(
status=status.HTTP_207_MULTI_STATUS,
data=response_data
)

return Response(status=status.HTTP_200_OK, data=response_data)

# def create(self, validated_data):
# # Custom creation logic here, if needed
# return Bco.objects.create(**validated_data)

# def update(self, instance, validated_data):
# # Custom update logic here, if needed
# for attr, value in validated_data.items():
# setattr(instance, attr, value)
# instance.save()
# return instance
10 changes: 7 additions & 3 deletions biocompute/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,20 @@ class Bco(models.Model):
BCO Object Identifier, and primary key
contents: JSONField
BCO JSON contents
authorized_group: ManyToManyField(Group)
String representing the django.contrib.auth.models.Group that 'owns' the object
owner_user = ForeignKey(User)
owner = ForeignKey(User)
String representing the django.contrib.auth.models.User that 'owns' the object
authorized_users: ManyToManyField(User)
String representing the User that has access to the object
authorized_group: ManyToManyField(Group)
String representing the Group that has access to the object
prefix: str
Prefix for the BCO
state:str
State of object. REFERENCED, PUBLISHED, DRAFT, and DELETE are currently accepted values.
last_update: DateTime
Date Time object for the last database change to this object
access_count: Int
number of times this object has been downloaded

"""

Expand Down
86 changes: 86 additions & 0 deletions biocompute/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env python3
# biocopmute/services.py

from django.db import transaction
from django.utils import timezone
from biocompute.models import Bco
from prefix.models import Prefix
from django.contrib.auth.models import Group, User
from rest_framework import serializers

"""BioCompute Services

Service functions for working with BCOs
"""

class BcoDraftSerializer(serializers.Serializer):
object_id = serializers.URLField(required=False)
contents = serializers.JSONField()
prefix = serializers.CharField(max_length=5, min_length=3, default="BCO")
authorized_groups = serializers.ListField(child=serializers.CharField(), required=False)
authorized_users = serializers.ListField(child=serializers.CharField(), required=False)

def validate(self, attrs):
errors = {}
request = self.context.get('request')
attrs["owner"] = request.user

#check for groups
if 'authorized_groups' in attrs:
for group in attrs['authorized_groups']:
try:
Group.objects.get(name=group)
except Exception as err:
errors['authorized_groups'] = f"Invalid group: {group}"
# check for users
if 'authorized_users' in attrs:
for user in attrs['authorized_users']:
try:
# import pdb; pdb.set_trace()
User.objects.get(username=user)
except Exception as err:
errors['authorized_users'] =f"Invalid user: {user}"

# Validate Prefix
try:
attrs['prefix_instance'] = Prefix.objects.get(prefix=attrs['prefix'])
except Prefix.DoesNotExist as err:
errors['prefix'] = 'Invalid prefix.'

# Validate object_id match
if 'object_id' in attrs and attrs['object_id'] != attrs['contents'].get('object_id', ''):
errors["object_id"] = "object_id does not match object_id in contents."

# Validate that object_id is unique
object_id = attrs['contents'].get('object_id', '')

if not Bco.objects.filter(object_id=object_id).exists():
pass
else:
errors["object_id"] = f"That object_id, {attrs['object_id']}, already exists."

if errors:
raise serializers.ValidationError(errors)

return attrs

@transaction.atomic
def create(self, validated_data):
# Remove the non-model field 'prefix' and use 'prefix_instance' instead
prefix_instance = validated_data.pop('prefix_instance', None)
validated_data.pop('prefix')
authorized_group_names = validated_data.pop('authorized_groups', [])
authorized_usernames = validated_data.pop('authorized_users', [])

bco_instance = Bco.objects.create(**validated_data, prefix=prefix_instance, last_update=timezone.now())

# Set ManyToMany relations
if authorized_group_names:
authorized_groups = Group.objects.filter(name__in=authorized_group_names)
bco_instance.authorized_groups.set(authorized_groups)

if authorized_usernames:
authorized_users = User.objects.filter(username__in=authorized_usernames)
bco_instance.authorized_users.set(authorized_users)

return bco_instance
7 changes: 6 additions & 1 deletion biocompute/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# biocompute/urls.py
"""BioCompute URLs
"""

from django.urls import path
from biocompute.apis import (
DraftsCreateApi
)

urlpatterns = [

path("objects/drafts/create/", DraftsCreateApi.as_view())
]
16 changes: 16 additions & 0 deletions config/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
# config/services.py

"""DB Level Services

Service functiontions for the entire DB
"""

def legacy_api_converter(data:dict) ->dict:
"""Legacy API converter

Used to remove the `POST_` object from requests.
"""

_, new_data = data.popitem()
return new_data
45 changes: 44 additions & 1 deletion config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,57 @@

Top level URL configuration for BCO DB. See `api.urls` for APIs
"""
import configparser
from django.conf import settings
from django.contrib import admin
from django.urls import path, include
from django.urls import path, include, re_path
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework import permissions
from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token

# Load the server config file.
server_config = configparser.ConfigParser()
server_config.read(settings.BASE_DIR + "/server.conf")

PUBLISH_ONLY = server_config["PUBLISHONLY"]["publishonly"]
VERSION = server_config["VERSION"]["version"]

ShcemaView = get_schema_view(
openapi.Info(
title="BioCompute Object Data Base API (BCODB API)",
default_version=VERSION,
description="A web application that can be used to create, store and "
"edit BioCompute objects based on BioCompute schema described "
"in the BCO specification document.",
terms_of_service="https://github.com/biocompute-objects/bco_api/blob/master/LICENSE",
contact=openapi.Contact(email="[email protected]"),
license=openapi.License(name="MIT License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)

urlpatterns = [
re_path(
r"^api/doc(?P<format>\.json|\.yaml)$",
ShcemaView.without_ui(cache_timeout=0),
name="schema-json",
),
path(
"api/docs/",
ShcemaView.with_ui("swagger", cache_timeout=0),
name="schema-swagger-ui",
),
path(
"api/redocs/",
ShcemaView.with_ui("redoc", cache_timeout=0),
name="schema-redoc",
),
path("api/admin/", admin.site.urls),
path("api/token/", obtain_jwt_token),
path("api/verify/", verify_jwt_token),
path("api/", include("authentication.urls")),
path("api/", include("search.urls")),
path("api/", include("biocompute.urls")),
]
34 changes: 29 additions & 5 deletions docs/refactor.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
# Notes for refactor
# Planned Changes for 24.04 release

## Changed items
- new_user -> NewUser
## Proposed changes

### Provide `One Click` examples that work for Swagger
- GlyGen and ARGOS do this already.

### simplify API models and processing
- previous model was based on multiple DB requests per object and each request could have buld sumissions
- We still want bulk submissions but validations and permissions should be checked by classes and serializers before pining DB
#### Examples:
- POST_api_objects_draft_create

### Handeling what will become `Legacy` requests
1. maintain the old code, and not publicize it
2. develope converter functions to process


### Refactor the groups user permissions
- previous model was based on additional objects for Groups and Users, This required the use of `signals` and meant that there were many additional objects in the DB each time a new user was created. This also led to dependancy issues which prohibits deleting anything.
- propose to elimiate the extra models

### Refactor the Prefix permission system
- Prefix required it's own two groups and the creation of 5 permissions for each prefix. Look up for authentication was time consuming and taxing on DB. Users also had no idea how to use the system, just what to do to make it work.
- Propose to add `authorized groups` to the prefix model. if it is empty then anyone can use it. If populated than only those in list can use it

### Refactor the BCO permission system
- same situation as prefix

## Items to look at later
- `authentication.apis.RegisterUserNoVerificationAPI` has no swagger or tests
- fix email and secrets
<<<<<<< Local Changes
- install a `test_template` for Swagger responses
- provide example values that are usable for testing APIs.
- certifying key for prefix as a JWT?
- owner = models.ForeignKey(
User,
on_delete=models.CASCADE,

- need tests for token
- unwanted swagger endpoints
- need tests for token
9 changes: 5 additions & 4 deletions prefix/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Prefix Admin Pannel
#!/usr/bin/env python3

"""Prefix URLs
"""

from django.contrib import admin
from prefix.models import Prefix

admin.site.register(Prefix)
from biocompute.apis import DraftsCreateApi

Loading
Loading