Skip to content

Commit

Permalink
Implement API endpoints for collections (#625)
Browse files Browse the repository at this point in the history
* Implement API endpoint for collections index

* Implement API endpoint for collection page

* Implement visibility conditions

* Extend github utility and code cleanup

* Update feature flags

* Update naming from slug to collection_name

Co-authored-by: Richa Agarwal <[email protected]>
  • Loading branch information
richaagarwal and Richa Agarwal authored Nov 4, 2022
1 parent f447a73 commit d8ef930
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 8 deletions.
15 changes: 14 additions & 1 deletion backend/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from flask import Flask, Response, jsonify, render_template
from flask_githubapp.core import GitHubApp
import yaml

from api.collections import get_collections, get_collection
from api.model import get_public_plugins, get_index, get_plugin, get_excluded_plugins, update_cache, \
move_artifact_to_s3, get_category_mapping, get_categories_mapping, get_manifest, get_installs, get_installs_stats, \
update_activity_data
Expand Down Expand Up @@ -126,6 +126,19 @@ def get_plugin_installs_stats(plugin: str) -> Response:
return jsonify(get_installs_stats(plugin))


@app.route('/collections')
def collections() -> Response:
return get_collections()


@app.route('/collections/<collection>')
def collection(collection: str) -> Response:
data = get_collection(collection)
if not data:
return app.make_response(("Collection does not exist", 404))
return data


@app.errorhandler(404)
def handle_exception(e) -> Response:
links = [rule.rule for rule in app.url_map.iter_rules()
Expand Down
70 changes: 70 additions & 0 deletions backend/api/collections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import yaml
from api.model import get_plugin
from utils.github import get_file

COLLECTIONS_CONTENTS = "https://api.github.com/repos/chanzuckerberg/napari-hub-collections/contents/collections"
COLLECTIONS_REPO = "https://github.com/chanzuckerberg/napari-hub-collections"
IMAGES_BASE_URL = "https://raw.githubusercontent.com/chanzuckerberg/napari-hub-collections/main/images/"


def get_collections():
json_file = get_file(download_url=COLLECTIONS_CONTENTS, file_format="json")
if not json_file:
return None
collections = []
for item in json_file:
collection_name = item.get("name").replace(".yml", "")
data = get_collection_preview(collection_name)
if data:
collections.append(data)
return collections


def get_yaml_data(collection_name, visibility_requirements):
"""Return collection's yaml data if it meets visibility requirements."""
filename = "collections/{collection_name}.yml".format(collection_name=collection_name)
yaml_file = get_file(download_url=COLLECTIONS_REPO, file=filename, branch="main")
if yaml_file:
data = yaml.safe_load(yaml_file)
if data and data.get("visibility", "public") in visibility_requirements:
data["cover_image"] = IMAGES_BASE_URL + data.get("cover_image")
return data
return None


def get_collection_preview(collection_name):
"""Return a subset of collection data for /collections."""
data = get_yaml_data(collection_name=collection_name, visibility_requirements=["public"])
if not data:
return None
return {
"title": data.get("title"),
"summary": data.get("summary"),
"cover_image": data.get("cover_image"),
"curator": data.get("curator"),
"symbol": collection_name,
}


def get_collection(collection_name):
"""Return full collection data for /collections/{collection}."""
data = get_yaml_data(collection_name=collection_name, visibility_requirements=["public", "hidden"])
if not data:
return None
# Get plugin-specific data
plugins = get_plugin_data(data["plugins"])
data["plugins"] = list(plugins)
return data


def get_plugin_data(collection_plugins):
"""Return plugin-specific data for each plugin specified in a collection."""
for collection_plugin in collection_plugins:
plugin_name = collection_plugin["name"]
plugin = get_plugin(plugin_name)
# Only include plugins that are set to public
if plugin and plugin.get("visibility", "public") == "public":
collection_plugin["summary"] = plugin.get("summary", "")
collection_plugin["authors"] = plugin.get("authors", [])
collection_plugin["display_name"] = plugin.get("display_name", "")
yield collection_plugin
19 changes: 13 additions & 6 deletions backend/utils/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@
}


def get_file(download_url: str, file: str, branch: str = 'HEAD') -> [dict, None]:
def get_file(
download_url: str, file: str = "", branch: str = "HEAD", file_format: str = ""
) -> [dict, None]:
"""
Get file from github.
:param download_url: github url to download from
:param file: filename to get
:param file: filename to get if specified
:param branch: branch name to use if specified
:param file_format: format to return if specified
:return: file context for the file to download
"""
local_workspace = os.getenv("GITHUB_WORKSPACE")
Expand All @@ -55,13 +58,17 @@ def get_file(download_url: str, file: str, branch: str = 'HEAD') -> [dict, None]
else:
return None

api_url = download_url.replace("https://github.com/",
"https://raw.githubusercontent.com/")
api_url = download_url.replace(
"https://github.com/", "https://raw.githubusercontent.com/"
)
if branch and file:
api_url = f"{api_url}/{branch}/{file}"
try:
url = f'{api_url}/{branch}/{file}'
response = requests.get(url, auth=auth)
response = requests.get(api_url, auth=auth)
if response.status_code != requests.codes.ok:
response.raise_for_status()
if file_format == "json":
return response.json()
return response.text
except HTTPError:
pass
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/utils/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const FEATURE_FLAGS = createFeatureFlags({
},

collections: {
environments: ['dev'],
environments: ['dev', 'staging', 'prod'],
},

activityDashboard: {
Expand Down

0 comments on commit d8ef930

Please sign in to comment.