Skip to content

Commit

Permalink
Merge pull request #10 from lsst-sqre/tickets/DM-46397
Browse files Browse the repository at this point in the history
tickets/DM-46397: touch up docs
  • Loading branch information
athornton authored Oct 17, 2024
2 parents e335638 + 94bc226 commit 6865255
Show file tree
Hide file tree
Showing 5 changed files with 15 additions and 7 deletions.
5 changes: 5 additions & 0 deletions changelog.d/20241017_114028_athornton_DM_46397.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- Delete the sections that don't apply -->

### Other changes

- Improve documentation.
2 changes: 1 addition & 1 deletion docs/_static/openapi.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"openapi": "3.1.0", "info": {"title": "Ghostwriter", "description": "URL shortener/personalizer for Phalanx\n\n[Return to Ghostwriter documentation](.)", "version": "0.1.dev57+g8fc122d.d20240917"}, "paths": {"/ghostwriter/": {"get": {"summary": "Application metadata", "description": "Document the top-level API here. By default it only returns metadata about the application.", "operationId": "get_index_ghostwriter__get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Index"}}}}}}}, "/ghostwriter/rewrite/{full_path}": {"get": {"summary": "Rewrite", "operationId": "rewrite_ghostwriter_rewrite__full_path__get", "parameters": [{"name": "full_path", "in": "path", "required": true, "schema": {"type": "string", "title": "The URL path to rewrite"}}], "responses": {"307": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}}, "components": {"schemas": {"HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "Index": {"properties": {"metadata": {"$ref": "#/components/schemas/Metadata", "title": "Package metadata"}}, "type": "object", "required": ["metadata"], "title": "Index", "description": "Metadata returned by the external root URL of the application.\n\nNotes\n-----\nAs written, this is not very useful. Add additional metadata that will be\nhelpful for a user exploring the application, or replace this model with\nsome other model that makes more sense to return from the application API\nroot."}, "Metadata": {"properties": {"name": {"type": "string", "title": "Application name", "examples": ["myapp"]}, "version": {"type": "string", "title": "Version", "examples": ["1.0.0"]}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description", "examples": ["Some package description"]}, "repository_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Repository URL", "examples": ["https://example.com/"]}, "documentation_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Documentation URL", "examples": ["https://example.com/"]}}, "type": "object", "required": ["name", "version"], "title": "Metadata", "description": "Metadata about a package."}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}}}}
{"openapi": "3.1.0", "info": {"title": "Ghostwriter", "description": "URL shortener/personalizer for Phalanx\n\n[Return to Ghostwriter documentation](.)", "version": "0.1.2.dev6+ge335638.d20241017"}, "paths": {"/ghostwriter/": {"get": {"summary": "Application metadata", "description": "Document the top-level API here. By default it only returns metadata about the application.", "operationId": "get_index_ghostwriter__get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Index"}}}}}}}, "/ghostwriter/rewrite/{full_path}": {"get": {"summary": "Rewrite", "operationId": "rewrite_ghostwriter_rewrite__full_path__get", "parameters": [{"name": "full_path", "in": "path", "required": true, "schema": {"type": "string", "title": "The URL path to rewrite"}}], "responses": {"307": {"description": "Successful Response"}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}}, "components": {"schemas": {"HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "Index": {"properties": {"metadata": {"$ref": "#/components/schemas/Metadata", "title": "Package metadata"}}, "type": "object", "required": ["metadata"], "title": "Index", "description": "Metadata returned by the external root URL of the application.\n\nNotes\n-----\nAs written, this is not very useful. Add additional metadata that will be\nhelpful for a user exploring the application, or replace this model with\nsome other model that makes more sense to return from the application API\nroot."}, "Metadata": {"properties": {"name": {"type": "string", "title": "Application name", "examples": ["myapp"]}, "version": {"type": "string", "title": "Version", "examples": ["1.0.0"]}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Description", "examples": ["Some package description"]}, "repository_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Repository URL", "examples": ["https://example.com/"]}, "documentation_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Documentation URL", "examples": ["https://example.com/"]}}, "type": "object", "required": ["name", "version"], "title": "Metadata", "description": "Metadata about a package."}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}}}}
10 changes: 6 additions & 4 deletions docs/hooks/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Hooks are units of work carried out before the redirect for the user's browser i

There is a list of hooks (possibly empty) for each route specified in the configuration.

Each hook is run in sequence.If any hook raises an exception, the user will receive an error rather than a redirect.
Each hook is run in sequence. If any hook raises an exception, the user will receive an error rather than a redirect.

Because (at least at present) hooks are, by definition, located inside the ``ghostwriter.hooks`` Python module namespace, any additions or modifications to hooks are code and repository changes.
Therefore, any proposed hooks or modififactions to existing ones will need to go through the SQuaRE PR process.
Expand All @@ -19,7 +19,7 @@ A hook is an ``async`` Python function that takes one argument, a ``ghostwriter.
To signal failure, a hook should raise an ``Exception``.

If a hook does not wish to modify the parameters used by future hooks or
route substitution, it should return None.
route substitution, it should return ``None``.

If it does return an object, that object will first be checked to ensure only the ``target`` and ``unique_id`` fields changed.
If any other field changed, an exception will be raised.
Expand All @@ -41,10 +41,12 @@ Two fields are only for use by the hook itself: those are ``target`` and ``uniqu
The ``target`` field will be injected when the hook is run, and the ``unique_id`` field may be populated.
These two are the only fields a hook is permitted to change if it returns a ``Parameters`` object.
The ``target`` may need to be rewritten to accomodate a ``unique_id``.
The motivation here is simply to avoid rewriting existing files: the correct response is context-dependendent, and might be to redirect to the existing file, but it equally well might be to create a new file under a different name.

The motivation here is simply to avoid rewriting existing files: the correct response is context-dependent, and might be to redirect to the existing file, but it equally well might be to create a new file under a different name.
In this case, the file name stem (that is, the part before the suffix) might need to be appended with a ``unique_id``, which could be (again, the best choice depends on context) a serial number, as in ``Untitled2.ipynb``, or a string representation of the date and time, or simply a UUID.
The ``unique_id`` can be any string legal in a filename, as long as the filename containing it will be distinct from any other filename in the directory.
Guaranteeing that it is unique is the job of the hook writer.

Given the context, the existing hooks do not worry much about race conditions.
If you are using the date or an incrementing integer...you are still in an RSP context, so it's very unlikely a user will go to the same redirected URL twice in the same microsecond, or even twice within the time it takes to write out a notebook. If you do have some high-frequency use case, a UUID would be a better choice.

Expand All @@ -62,7 +64,7 @@ With great power comes great responsibility.
Example
=======

The ``portal_query`` hook provides a moderately complex workflow.
The `portal_query <https://github.com/lsst-sqre/ghostwriter/blob/main/src/ghostwriter/hooks/portal_query.py>`__ hook provides a moderately complex workflow.

Note that it assumes a running Lab for the user.

Expand Down
3 changes: 2 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ There are two initial use cases: the first is to allow specification of a generi

The second is to allow appending a Portal UWS query ID to a generic route, and have that open a templated notebook in a user's Lab containing retrieval of that query and conversion to a table.

This is accomplished through a configuration file specifying the routes that get redirected and personalized, and for each route a list of hooks applied to do work before the redirection is implemented.
This is accomplished through a configuration file specifying the routes
that get redirected and personalized. For each route, a sequence of hooks, each applied to do work and possibly further transform the redirection target, is run. Finally the redirection occurs.

.. grid:: 1

Expand Down
2 changes: 1 addition & 1 deletion docs/routing/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The ``/nb`` route will carry the user to the Hub.

From there, the ``/user/${user}/lab/tree`` route will take the user to a particular file within their space.

The rest of the path specifies the directory in which the notebook can be found, and constructs the filename and an ``.ipynb`` suffix.
The rest of the path specifies the directory in which the notebook can be found, and constructs the filename, adding the suffix ``.ipynb``.

For each route, the ``ensure_running_lab`` hook is run, which does exactly what you would expect from the name: if a user does not have a running lab, a new lab, running the currently-recommended image in a medium-size container (these parameters were chosen because they are those suggested for running tutorial notebooks), will be spawned on the user's behalf.

Expand Down

0 comments on commit 6865255

Please sign in to comment.