File size: 5,063 Bytes
d1ceb73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
"""Tornado handlers for extension management."""

# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

import dataclasses
import json
from urllib.parse import urlencode, urlunparse

from jupyter_server.base.handlers import APIHandler
from tornado import web

from jupyterlab.extensions.manager import ExtensionManager


class ExtensionHandler(APIHandler):
    def initialize(self, manager: ExtensionManager):
        super().initialize()
        self.manager = manager

    @web.authenticated
    async def get(self):
        """GET query returns info on extensions

        Query arguments:
            refresh: [optional] Force refreshing the list of extensions - ["0", "1"]; default 0
            query: [optional] Query to search for extensions - default None (i.e. returns installed extensions)
            page: [optional] Result page - default 1 (min. 1)
            per_page: [optional] Number of results per page - default 30 (max. 100)
        """
        query = self.get_argument("query", None)
        page = max(1, int(self.get_argument("page", "1")))
        per_page = min(100, int(self.get_argument("per_page", "30")))
        if self.get_argument("refresh", "0") == "1":
            await self.manager.refresh(query, page, per_page)

        extensions, last_page = await self.manager.list_extensions(query, page, per_page)

        self.set_status(200)
        if last_page is not None:
            links = []
            query_args = {"page": last_page, "per_page": per_page}
            if query is not None:
                query_args["query"] = query
            last = urlunparse(
                (
                    self.request.protocol,
                    self.request.host,
                    self.request.path,
                    "",
                    urlencode(query_args, doseq=True),
                    "",
                )
            )
            links.append(f'<{last}>; rel="last"')
            if page > 1:
                query_args["page"] = max(1, page - 1)
                prev = urlunparse(
                    (
                        self.request.protocol,
                        self.request.host,
                        self.request.path,
                        "",
                        urlencode(query_args, doseq=True),
                        "",
                    )
                )
                links.append(f'<{prev}>; rel="prev"')
            if page < last_page:
                query_args["page"] = min(page + 1, last_page)
                next_ = urlunparse(
                    (
                        self.request.protocol,
                        self.request.host,
                        self.request.path,
                        "",
                        urlencode(query_args, doseq=True),
                        "",
                    )
                )
                links.append(f'<{next_}>; rel="next"')
            query_args["page"] = 1
            first = urlunparse(
                (
                    self.request.protocol,
                    self.request.host,
                    self.request.path,
                    "",
                    urlencode(query_args, doseq=True),
                    "",
                )
            )
            links.append(f'<{first}>; rel="first"')
            self.set_header("Link", ", ".join(links))

        self.finish(json.dumps(list(map(dataclasses.asdict, extensions))))

    @web.authenticated
    async def post(self):
        """POST query performs an action on a specific extension

        Body arguments:
            {
                "cmd": Action to perform - ["install", "uninstall", "enable", "disable"]
                "extension_name": Extension name
                "extension_version": [optional] Extension version (used only for install action)
            }
        """
        data = self.get_json_body()
        cmd = data["cmd"]
        name = data["extension_name"]
        version = data.get("extension_version")
        if cmd not in ("install", "uninstall", "enable", "disable") or not name:
            raise web.HTTPError(
                422,
                f"Could not process instruction {cmd!r} with extension name {name!r}",
            )

        ret_value = None
        try:
            if cmd == "install":
                ret_value = await self.manager.install(name, version)
            elif cmd == "uninstall":
                ret_value = await self.manager.uninstall(name)
            elif cmd == "enable":
                ret_value = await self.manager.enable(name)
            elif cmd == "disable":
                ret_value = await self.manager.disable(name)
        except Exception as e:
            raise web.HTTPError(500, str(e)) from e

        if ret_value.status == "error":
            self.set_status(500)
        else:
            self.set_status(201)
        self.finish(json.dumps(dataclasses.asdict(ret_value)))


# The path for lab extensions handler.
extensions_handler_path = r"/lab/api/extensions"