Skip to content
This repository has been archived by the owner on May 9, 2022. It is now read-only.

Commit

Permalink
Merge pull request #3 from lsst-sqre/tickets/DM-34030
Browse files Browse the repository at this point in the history
DM-34030: Implement auto-refreshing notebooks
  • Loading branch information
jonathansick authored Mar 31, 2022
2 parents 6072fc3 + 538eb83 commit 1f01762
Show file tree
Hide file tree
Showing 12 changed files with 297 additions and 29 deletions.
30 changes: 30 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "thursday"
time: "09:00"
timezone: America/Toronto
reviewers:
- "jonathansick"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
time: "09:00"
timezone: America/Toronto
# Generally this means we'll get minor and patch updates, but we'll
# manually need to roll out major version changes.
versioning-strategy: "lockfile-only"
registries:
- npm-github
reviewers:
- "jonathansick"
registries:
npm-github:
type: npm-registry
url: https://npm.pkg.github.com
# lsst-sqre org secret (for Dependabot)
token: ${{ secrets.READONLY_PACKAGES_GITHUB_TOKEN }}
18 changes: 4 additions & 14 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,24 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Read .nvmrc
id: node_version
run: echo ::set-output name=NODE_VERSION::$(cat .nvmrc)

- name: Set up node
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ steps.node_version.outputs.NODE_VERSION }}
cache: 'npm'

- name: Authenticate GitHub Packages
run: |
echo "//npm.pkg.github.com/:_authToken=${NPM_PKG_TOKEN}" > ~/.npmrc
env:
NPM_PKG_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Cache downloaded Node.js modules
uses: actions/cache@v2
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
# Caching node_modules itself isn't compatible with npm ci
path: ~/.npm
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-node-
${{ runner.OS }}-
- name: Install npm packages
run: npm ci

Expand All @@ -74,7 +64,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Define the Docker tag
id: vars
Expand Down
2 changes: 0 additions & 2 deletions README.md

This file was deleted.

119 changes: 119 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
.. image:: https://github.com/lsst-sqre/times-square-ui/actions/workflows/ci.yaml/badge.svg
:target: https://github.com/lsst-sqre/times-square-ui/actions/

###############
Times Square UI
###############

**The front-end web interface for Times Square, a Rubin Science Platform (RSP) service for displaying parameterized Jupyter Notebooks as websites.**

Excellent applications for Times Square include:

- Engineering dashboards
- Quick-look data previewing
- Reports that incorporate live data sources

The design and architecture of Times Square is described in `SQR-062: The Times Square service for publishing parameterized Jupyter Notebooks in the Rubin Science platform <https://sqr-062.lsst.io>`__.
Times Square uses Noteburst (`GitHub <https://github.com/lsst-sqre/noteburst>`__, `SQR-065 <https://sqr-065.lsst.io>`__ to execute Jupyter Notebooks in Nublado (JupyterLab) instances, thereby mechanizing the RSP's notebook aspect.

This Times Square API service is developed separately at `https://github.com/lsst-sqre/times-square <https://github.com/lsst-sqre/times-square>`__.
You can find the RSP deployment configuration in Phalanx's `services/times-square/ <https://github.com/lsst-sqre/phalanx/tree/master/services/times-square>`__ directory.

Technology stack
================

- The site is built with Next.js_ and React_.
Next.js_ allows the site to be dynamically configured for different Science Platform deployments.

- Styling is done through styled-components_ (along with global CSS).

Development workflow primer
===========================

Configure npm to use packages from @lsst-sqre
---------------------------------------------

Times Square UI uses npm packages published to the GitHub Package Registry in the ``lsst-sqre`` org.
Although they're publicly-available, you will need a `GitHub Personal Access Token <https://github.com/settings/tokens/new>`__ with ``read:packages``.

Add an `@lsst-sqre` registry entry to your `~/.npmrc` file using the token you created::

@lsst-sqre:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=<...>

Node version
------------

The Node.js version used by this this project is intended to be built with a Node.js version that's encoded in the `.nvmrc <./.nvmrc>`__ file.
To adopt this node version, we recommend `installing and using the node version manager <https://github.com/nvm-sh/nvm>`__.

Then you can use the preferred node version by running ``nvm`` from the project root::

nvm use

Install locally
---------------

Install the JavaScript packages::

npm install

Install git hooks
-----------------

Git hooks allow you to automatically lint and format code with eslint and prettier on each commit.
These hooks are managed by `husky <https://typicode.github.io/husky/#/>`_, and should be installed automatically when you install Squareone locally.
If not, you can manually install the hooks::

husky install

Manual linting and formatting
-----------------------------

You can also manually lint and format code.

Lint and format JavaScript via `next lint <>`__::

npm run lint

Check formatting other types of code with Prettier_::

npm run format:check

Or automatically fix files::

npm run format

Start the development server
----------------------------

::

npm run dev

View the site at http://localhost:3000/times-square/.
This site auto-updates when running with the development server.

`API routes <https://nextjs.org/docs/api-routes/introduction>`_ are accessed on http://localhost:3000/times-square/api/*.
The ``pages/api`` directory is mapped to ``/api/*``.
Files in this directory are treated as `API routes`_ instead of React pages.
The purpose of the ``pages/api/dev`` endpoints are to mock external services in the RSP; see the re-writes in `next.config.js`.
Create a production build
-------------------------

This builds the optimized application::

npm run build

You can serve the production build locally::

npm run serve

.. _Next.js: https://nextjs.org
.. _Prettier: https://prettier.io/
.. _Rubin Observatory: https://www.lsst.org
.. _React: https://reactjs.org
.. _styled-components: https://styled-components.com
.. _Semaphore: https://github.com/lsst-sqre/semaphore
.. _Phalanx: https://phalanx.lsst.io
54 changes: 54 additions & 0 deletions components/notebookIframe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* The NotebookIframe controls the iframe with HTML content
* from Times Square with a notebook render.
*/

import styled from 'styled-components';

import useHtmlStatus from '../hooks/htmlStatus';

const StyledIframe = styled.iframe`
--shadow-color: 0deg 0% 74%;
--shadow-elevation-medium: 0.1px 0.7px 0.9px hsl(var(--shadow-color) / 0.16),
0.4px 2.4px 3px -0.6px hsl(var(--shadow-color) / 0.2),
0.8px 5.3px 6.7px -1.1px hsl(var(--shadow-color) / 0.24),
1.9px 11.9px 15px -1.7px hsl(var(--shadow-color) / 0.28);
border: 0px solid black;
box-shadow: var(--shadow-elevation-medium);
width: 100%;
height: 100%;
`;

export default function NotebookIframe({
tsHtmlUrl,
tsHtmlStatusUrl,
parameters,
}) {
const htmlUrl = new URL(tsHtmlUrl);
parameters.forEach((item) => htmlUrl.searchParams.set(item[0], item[1]));

const htmlStatus = useHtmlStatus(tsHtmlStatusUrl, parameters);

if (htmlStatus.error) {
return (
<div>
<p>Error contacting API at {`${tsHtmlStatusUrl}`}</p>
</div>
);
}

if (htmlStatus.loading) {
return (
<div>
<p>Loading...</p>
</div>
);
}

return (
<StyledIframe
src={htmlStatus.htmlUrl || htmlUrl.toString()}
key={htmlStatus.iframeKey}
></StyledIframe>
);
}
30 changes: 30 additions & 0 deletions hooks/htmlStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* useHtmlStatus hook fetches data from the Times Square
* /v1/pages/:page/htmlstatus endpoint using the SWR hook to enable
* dynamic refreshing of data about a page's HTML rendering.
*/

import useSWR from 'swr';

const fetcher = (...args) => fetch(...args).then((res) => res.json());

function useHtmlStatus(htmlStatusUrl, parameters) {
const url = new URL(htmlStatusUrl);
parameters.forEach((item) => url.searchParams.set(item[0], item[1]));
const fullHtmlStatusUrl = url.toString();

const { data, error } = useSWR(fullHtmlStatusUrl, fetcher, {
refreshInterval: 1000, // ping every 1 second while brower in focus
});

return {
error: error,
loading: !error && !data,
htmlAvailable: data ? data.available : false,
htmlHash: data ? data.html_hash : null,
htmlUrl: data ? data.html_url : null,
iframeKey: data && data.available ? data.html_hash : 'html-not-available',
};
}

export default useHtmlStatus;
4 changes: 4 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ module.exports = (phase, { defaultConfig }) => {
source: '/api/v1/pages/:page/html',
destination: '/api/dev/times-square/v1/pages/:page/html',
},
{
source: '/api/v1/pages/:page/htmlstatus',
destination: '/api/dev/times-square/v1/pages/:page/htmlstatus',
},
{
source: '/api/v1/pages/:page',
destination: '/api/dev/times-square/v1/pages/:page',
Expand Down
17 changes: 16 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
"normalize.css": "^8.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"styled-components": "^5.3.3"
"styled-components": "^5.3.3",
"swr": "^1.2.2"
},
"devDependencies": {
"babel-plugin-styled-components": "^2.0.2",
Expand Down
1 change: 1 addition & 0 deletions pages/api/dev/times-square/v1/pages/[page].js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default function handler(req, res) {
source_url: `${pageBaseUrl}/source`,
rendered_url: `${pageBaseUrl}/rendered`,
html_url: `${pageBaseUrl}/html`,
html_status_url: `${pageBaseUrl}/htmlstatus`,
parameters: {
a: {
type: 'number',
Expand Down
26 changes: 26 additions & 0 deletions pages/api/dev/times-square/v1/pages/[page]/htmlstatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Mock Times Square API endpoint: /times-square/api/v1/pages/:page/htmlstatus
*/
import getConfig from 'next/config';

export default function handler(req, res) {
const { page, a } = req.query;
const { publicRuntimeConfig } = getConfig();
const { timesSquareApiUrl } = publicRuntimeConfig;

const pageBaseUrl = `${timesSquareApiUrl}/v1/pages/${page}`;

const content = {
available: a != '2', // magic value to toggle status modes
html_url: `${pageBaseUrl}/html?a={a}`,
html_hash: a != '2' ? '12345' : null,
};

console.log(content);

console.log('Pinged status');

res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(content));
}
Loading

0 comments on commit 1f01762

Please sign in to comment.