## Questions:
* How should registration of SCRUD resource work in Django
* For example, where should the registration happen, and what should that imply?
### Ideal Answer
Start with a `resource_types` table and autoload that from `conf.py` based on static resources in the application. This would require metaclasses that know how to read that table and generate the models, serializers, and Django REST Framework views dynamically based on the configured SCRUD resources.
#### Stepping Stones Toward Ideal for django
1. Define a model for `Resource`
a. It has the fields `schema` and `context` that are both `Resource` links. Nullable.
b. It has a field `content` that is the JSON for the content
2. Define a serializer for `Resource`.
3. Define a DRF view for `Resource`.
4. Define a way to "register" `Resource` instances based on static files in the app.
```python
#Django, phase 1
class Resource(Model):
# The actual JSON content for this resource
content = JsonField()
# JSON-Schema for this resource
schema = ForeignKey(Resource, nullable=True)
# JSON-LD for this resource
context = ForeignKey(Resource, nullable=True)
modified_at = DateTime()
etag = StringField()
```
```python
#Django, phase 2, we could start here...
# this will support something like GET /resource_type/schema?uri=http://schema.org/person
# to get the json-schema for Person
# or GET /resource_type/context?uri=http://schema.org/person
# to get the json-ld for Person
class ResourceType(Model):
type_uri: StringField()
# Necessary IFF there is a collection endpoint
# associated with this ResourceType
slug: StringField(nullable=true)
# require either schema or schema_uri
schema = ForeignKey(Resource, nullable=True)
schema_uri = StringField(nullable=True)
# require either context or context_uri
context = ForeignKey(Resource, nullable=True)
context_uri = StringField(nullable=True)
class Resource(Model):
# The actual JSON content for this resource
content = JsonField()
resource_type = ForeignKey(ResourceType)
modified_at = DateTime()
etag = StringField()
```
#### FASTAPI approach, start with ABC typing
1. Generate the schema and context from the types!
```python
class Resource(Model):
content: Content
schema: Schema
context: Context
modified_at: DateTime
etag: String
```
```python
from scrud_django import register_resource_type
...
# This results in the creation of the model, serializer, view, etc
# for the resource specified by the json schema and json-ld context
register_resource_type(
'my_resources',
resource_type_uri,
json_schema,
json_ld_context)
register_resource_type(
collection_slug='mystuff',
schema=json_schema,
context=json_ld_context,
mixins=[my_query_mixin, my_search_mixin]
)
```
```json
[{
"url": {}
, "last_modified": ""
, "etag": ""
, "content": {
"prop1": "foo"
, "prop2": "bar"
, "really_big_property":
"url": "/path/to/big/image.png"
}
}
, {}
]
```
```javascript
contextRdf.lookup("foo.bar[2].baz")
-> "http://api.openteams.com/json-ld/baz"
contextRdf.supertypes("http://api.openteams.com/json-ld/baz")
-> ["http://schema.org/Person"]
```
```json
{
"next": ""
, "prev": ""
, "results": [{
"url": ""
, "last_modified": ""
, "etag": ""
, "content": { # what is the json-ld and json schema of this?
...
}
}]
}
```
```json
{
"context": "jsonld"
, "@id": "https://api.openteams.com/json-ld/PeopleQueryResult"
, "next": {}
, "prev": {}
"results": {
"@id": "https://api.openteams.com/json-ld/PeopleQueryMatch"
"isa": "https://api.openteams.com/json-ld/EnvelopeArray"
"contains": "https://api.openteams.com/json-ld/Person"
"schema": "https://api.openteams.com/schema/Person"
}
}
```
# Envelopes
We're going to need a json-ld approach to indicating that a property is an envelope. What's more, we'll need to indicate the type of the resource contained within the envelope!
## *Key Thing to Remember*
The envelopes have caching headers! So, when processing a resource, if the json-ld context specifies `Envelope` or `EnvelopeArray` properties - cache their contents as resources by grabbing the caching headers in the containing envelope.
# Resource with an envelope
Let's imagine a resource that contains a "status" resource in an envelope.
In this example, this composite resource contains a `status` that is another resource.
```json
{
...
"status": {
"url": "/thing/42/status"
, "last_modified": ""
, "etag": ""
, "content": {
"state": "in-progress"
}
}
...
}
```
So, what would the json-ld look like?
```json
{
"openteams": "https://api.openteams.com/json-ld/"
...
"status": {
"@type": "https://api.openteams.com/json-ld/Envelope"
, "content": {
"@type": "https://api/openteams.com/json-ld/Status"
}
}
}
```
So... if I have this right, the `status.content` property is a `https://api.openteams.com/json-ld/Status` resource.
## What about collections?
Let's say I had a collection of Partner Profile resources
```json
{
...
"profiles": [
{
"url": ""
, "last_modified": ""
, "etag": ""
, "content": {
...
}
}
]
}
```
How do we indicate that the elements of the `profiles` array are of resource type `https://api.openteams.com/json-ld/PartnerProfile`?
```json
{
...
"profiles": {
"@type": "https://api.openteams.com/json-ld/EnvelopeArray"
, "content": "https://api.openteams.com/json-ld/PartnerProfile"
}
}
```
# Service Catalog
For the Django app, we're going to need a way to advertise endpoints available from the app to bootstrap the client.
GET this from `https://backend.openteams.com/services`:
```json
{
"https://api.openteams.com/json-ld/PartnerProfile-Listing": "https://backend.openteams.com/partner-profiles"
, ...
}
```
The above indicates that there is a "Partner Profile Listing" resource (`https://api.openteams.com/json-ld/PartnerProfile-Listing`) located at `https://backend.openteams.com/partner-profiles`.
## What about just an array?
```json
["https://backend.openteams.com/partner-profiles"
, "https://backend.openteams.com/client-profiles"
, "https://backend.openteams.com/user"
, "https://backend.openteams.com/initiatives"
, "https://backend.openteams.com/projects"
]
```
The problem is in bootstrapping the client - you would have to perform OPTIONS on every element in the list to get the mapping needed. Let's assume a "configured" client - that is, it's set up to start from somewhere, like, listing several of the items. It's poor form to perform a bunch of calls to build the RDF Resource Type -> URL map just to get the client started.