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

feat: publisher dashboard #15

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

Conversation

tdstein
Copy link

@tdstein tdstein commented Jan 22, 2025

The Publisher Dashboard is an experimental project that tries a different way of managing Connect projects.

The server application (app.py) is built using FastAPI. The server logic utilizes Connect OAuth to present the viewer with their own information instead of relying on the Content owner's credentials. When running outside of Connect (e.g., development), the environment variables CONNECT_SERVER and CONNECT_API_KEY are utilized to authenticate with Connect.

The client application (src/index.js) is developed using the Mithril.js framework. Mithril is a modern client-side JavaScript framework "used by companies like Vimeo and Nike, and open source platforms like Lichess.".

See README.md for additional developer instructions.

A production build is available at https://rsc.radixu.com/content/cd4a05a2-992a-43b9-83ab-30c4de7f302a/.

The application is intended to be utilized in "Open Solo." Ultimately, it could serve as the DashboardPath.

@app.get("/api/contents")
async def contents(posit_connect_user_session_token: str = Header(None)):
if posit_connect_user_session_token:
viewer = client.with_user_session_token(posit_connect_user_session_token)
Copy link

@mconflitti-pbc mconflitti-pbc Jan 23, 2025

Choose a reason for hiding this comment

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

This call should probably be cached. Saves a round trip once the app has made at least one call for that specific user session token.

from cachetools import TTLCache, cached

# Create cache with TTL=1hour and unlimited size
client_cache = TTLCache(maxsize=float('inf'), ttl=3600)

@cached(client_cache)
def get_visitor_client(token: str | None) -> connect.Client:
    """Create and cache API client per token with 1 hour TTL"""
    if token:
        return client.with_user_session_token(token)
    else:
        return client

Copy link

@mconflitti-pbc mconflitti-pbc left a comment

Choose a reason for hiding this comment

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

Would be great to address the caching comment, but otherwise looks great!

@@ -0,0 +1,4 @@
# requirements.txt auto-generated by Posit Publisher
# using /Users/me/Projects/connect-extensions/extensions/content-manager/.venv/bin/python
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you demo for me on Posit Publisher?

Comment on lines +78 to +82
for _ in range(30):
job = content.jobs.find(process_id)
if job["status"] != 0:
return
sleep(1)
Copy link
Contributor

Choose a reason for hiding this comment

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

If this used an async sleep, would other routes be able to work while sleeping?

Copy link
Contributor

Choose a reason for hiding this comment

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

In shiny, the reactive graph is typically locked up. So there isn't a real advantage within Shiny unless you use extended tasks.

Choose a reason for hiding this comment

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

I think turning this into a background task would be a good optimization though maybe unnecessary for this example? But agree that at very least, this is switched to asyncio.sleep(1) since fastapi is using an async event loop for these requests. Pretty sure a sleep will block other stuff

Comment on lines +1 to +20
# Configuration file generated by Posit Publisher.
# Please review and modify as needed. See the documentation for more options:
# https://github.com/posit-dev/publisher/blob/main/docs/configuration.md
'$schema' = 'https://cdn.posit.co/publisher/schemas/posit-publishing-schema-v3.json'
type = 'python-fastapi'
entrypoint = 'app.py'
validate = true
files = [
'/app.py',
'/requirements.txt',
'/.posit/publish/Publisher Dashboard-V7KE.toml',
'/.posit/publish/deployments/deployment-VF7C.toml',
'/dist'
]
title = 'Publisher Dashboard'

[python]
version = '3.12.5'
package_file = 'requirements.txt'
package_manager = 'pip'

Choose a reason for hiding this comment

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

Does it make sense to to check in the .posit folder generally here?

Is this for user adoption or internal deployments?

Choose a reason for hiding this comment

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

This folder contains deployment option configs and deployment records.

IMHO if connect-extensions is intended as a private set of example extension apps for Connect devs, this could be checked in. If this is meant to be a public-facing repo, we shouldn't check in our deployment records, as they won't be useful to anyone.

Copy link

@toph-allen toph-allen left a comment

Choose a reason for hiding this comment

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

Looks v nice! I think we shouldn't check in .posit/ probably, but I'm not going to raise a stink about it if we do. :)

Comment on lines +1 to +20
# Configuration file generated by Posit Publisher.
# Please review and modify as needed. See the documentation for more options:
# https://github.com/posit-dev/publisher/blob/main/docs/configuration.md
'$schema' = 'https://cdn.posit.co/publisher/schemas/posit-publishing-schema-v3.json'
type = 'python-fastapi'
entrypoint = 'app.py'
validate = true
files = [
'/app.py',
'/requirements.txt',
'/.posit/publish/Publisher Dashboard-V7KE.toml',
'/.posit/publish/deployments/deployment-VF7C.toml',
'/dist'
]
title = 'Publisher Dashboard'

[python]
version = '3.12.5'
package_file = 'requirements.txt'
package_manager = 'pip'

Choose a reason for hiding this comment

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

This folder contains deployment option configs and deployment records.

IMHO if connect-extensions is intended as a private set of example extension apps for Connect devs, this could be checked in. If this is meant to be a public-facing repo, we shouldn't check in our deployment records, as they won't be useful to anyone.

}

this._fetch = m
.request({ method: "GET", url: `api/contents/${id}/releases` })

Choose a reason for hiding this comment

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

This is probably a dumb question, but the Python FastAPI app is super simple — is concrete technical constraint that that the JavaScript app couldn't make requests to Connect directly? I guess in that case it'd be published as a "Static" content item, i.e. just a set of web pages available at a URL under Connect.

I guess the FastAPI app allows you to have a simple surface that you're programming against and lets you do some reshaping of the data, and I know the whole goal of this project is to use the SDKs — I'm just asking out of curiosity. :)

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

Successfully merging this pull request may close these issues.

4 participants