async def view_graphql_schema(request, datasette): database = request.url_vars.get("database") try: datasette.get_database(database) except KeyError: raise NotFound("Database does not exist") schema = await schema_for_database_via_cache(datasette, database=database) return Response.text(print_schema(schema))
def robots_txt(datasette): config = datasette.plugin_config("datasette-block-robots") or {} literal = config.get("literal") disallow = [] if literal: return Response.text(literal) disallow = config.get("disallow") or [] if isinstance(disallow, str): disallow = [disallow] allow_only_index = config.get("allow_only_index") if allow_only_index: for database_name in datasette.databases: if database_name != "_internal": disallow.append(datasette.urls.database(database_name)) if not disallow: disallow = ["/"] lines = ["User-agent: *" ] + ["Disallow: {}".format(item) for item in disallow] return Response.text("\n".join(lines))
def handle_exception(datasette, request, exception): datasette._exception_hook_fired = (request, exception) if request.args.get("_custom_error"): return Response.text("_custom_error") elif request.args.get("_custom_error_async"): async def inner(): return Response.text("_custom_error_async") return inner
async def manage_db_group(scope, receive, datasette, request): db_name = unquote_plus(request.url_vars["database"]) if not await datasette.permission_allowed( request.actor, "live-permissions-edit", db_name, default=False ): raise Forbidden("Permission denied") db = get_db(datasette) group_id = None results = db["groups"].rows_where("name=?", [f"DB Access: {db_name}"]) for row in results: group_id = row["id"] break assert db_name in datasette.databases, "Non-existant database!" if not group_id and db_name not in BLOCKED_DB_ACTIONS: db["groups"].insert({ "name": f"DB Access: {db_name}", }, pk="id", replace=True) return await manage_db_group(scope, receive, datasette, request) if request.method in ["POST", "DELETE"]: formdata = await request.post_vars() user_id = formdata["user_id"] if request.method == "POST": db["group_membership"].insert({ "group_id": group_id, "user_id": user_id, }, replace=True) elif request.method == "DELETE": db["group_membership"].delete((group_id, user_id)) return Response.text('', status=204) else: raise NotImplementedError(f"Bad method: {request.method}") perms_query = """ select distinct user_id as id, lookup, value, description from group_membership join users on group_membership.user_id = users.id where group_membership.group_id=? """ users = db.execute(perms_query, (group_id,)) return Response.html( await datasette.render_template( "database_management.html", { "database": db_name, "users": users, }, request=request ) )
def register_routes(datasette): config = datasette.plugin_config("register-route-demo") if not config: return path = config["path"] def new_table(request): return Response.text("/db/table: {}".format( sorted(request.url_vars.items()))) return [ (r"/{}/$".format(path), lambda: Response.text(path.upper())), # Also serves to demonstrate over-ride of default paths: (r"/(?P<db_name>[^/]+)/(?P<table_and_format>[^/]+?$)", new_table), ]
async def perms_crud(scope, receive, datasette, request): table = request.url_vars["table"] default_next = datasette.urls.path(f"/live_permissions/{table}") next = request.args.get("next", default_next) obj_id = request.url_vars["id"] if not await datasette.permission_allowed( request.actor, "live-permissions-edit", default=False ): raise Forbidden("Permission denied") assert table and obj_id, "Invalid URL" assert request.method in ["POST", "DELETE"], "Bad method" assert table in KNOWN_TABLES, "Bad table name provided" db = get_db(datasette) # POST is just dual update/create (depending on if id=="new") if request.method == "POST": formdata = await request.post_vars() if "csrftoken" in formdata: del formdata["csrftoken"] pk = "id" if table == "group_membership": pk = ("group_id", "user_id") db[table].insert( formdata, pk=pk, alter=False, replace=False ) return Response.redirect(next) elif request.method == "DELETE": try: obj_id = int(obj_id) except ValueError: obj_id = tuple(int(i) for i in obj_id.split(",")) db[table].delete(obj_id) return Response.text('', status=204) else: raise NotImplementedError("Bad HTTP method!")
async def one(datasette): return Response.text( (await datasette.get_database().execute("select 1 + 1")).first()[0] )
def new_table(request): return Response.text("/db/table: {}".format( sorted(request.url_vars.items())))
async def options(self, request, *args, **kwargs): r = Response.text("ok") if self.ds.cors: add_cors_headers(r.headers) return r
def test_response_text(): response = Response.text("Hello from text") assert 200 == response.status assert "Hello from text" == response.body assert "text/plain; charset=utf-8" == response.content_type
async def delete(self, request, *args, **kwargs): return Response.text("Method not allowed", status=405)
def robots_txt(): return Response.text("Sitemap: https://www.niche-museums.com/sitemap.xml")
async def view_graphql(request, datasette): if request.method == "OPTIONS": return Response.text("ok", headers=CORS_HEADERS if datasette.cors else {}) body = await post_body(request) database = request.url_vars.get("database") try: datasette.get_database(database) except KeyError: raise NotFound("Database does not exist") if not body and "text/html" in request.headers.get("accept", ""): return Response.html( await datasette.render_template("graphiql.html", { "database": database, }, request=request), headers=CORS_HEADERS if datasette.cors else {}, ) schema = await schema_for_database_via_cache(datasette, database=database) if request.args.get("schema"): return Response.text(print_schema(schema)) incoming = {} if body: incoming = json.loads(body) query = incoming.get("query") variables = incoming.get("variables") operation_name = incoming.get("operationName") else: query = request.args.get("query") variables = request.args.get("variables", "") if variables: variables = json.loads(variables) operation_name = request.args.get("operationName") if not query: return Response.json( {"error": "Missing query"}, status=400, headers=CORS_HEADERS if datasette.cors else {}, ) config = datasette.plugin_config("datasette-graphql") or {} context = { "time_started": time.monotonic(), "time_limit_ms": config.get("time_limit_ms") or DEFAULT_TIME_LIMIT_MS, "num_queries_executed": 0, "num_queries_limit": config.get("num_queries_limit") or DEFAULT_NUM_QUERIES_LIMIT, } result = await graphql( schema, query, operation_name=operation_name, variable_values=variables, context_value=context, executor=AsyncioExecutor(), return_promise=True, ) response = {"data": result.data} if result.errors: response["errors"] = [format_error(error) for error in result.errors] return Response.json( response, status=200 if not result.errors else 500, headers=CORS_HEADERS if datasette.cors else {}, )
async def inner(): return Response.text("_custom_error_async")
async def two(request): name = request.url_vars["name"] greeting = request.args.get("greeting") return Response.text("{} {}".format(greeting, name))
async def two(request): name = request.url_vars["name"] greeting = request.args.get("greeting") return Response.text(f"{greeting} {name}")
def render_yaml(rows): return Response.text(dump([dict(r) for r in rows], sort_keys=False))
def register_routes(datasette): config = datasette.plugin_config("register-route-demo") if not config: return path = config["path"] return [(r"/{}/$".format(path), lambda: Response.text(path.upper()))]
def options(self, request, *args, **kwargs): r = Response.text("ok") if self.ds.cors: r.headers["Access-Control-Allow-Origin"] = "*" return r
async def robots(): return Response.text(robots_text)