Skip to content

Commit

Permalink
fix:use permalink for query sharing
Browse files Browse the repository at this point in the history
  • Loading branch information
LevisNgigi committed Jan 20, 2025
1 parent bb871c6 commit a361eda
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 114 deletions.
44 changes: 26 additions & 18 deletions superset-frontend/src/pages/SavedQueryList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,25 +224,33 @@ function SavedQueryList({
menuData.buttons = subMenuButtons;

// Action methods
const openInSqlLab = (id: number, openInNewWindow: boolean) => {
if (openInNewWindow) {
window.open(`/sqllab?savedQueryId=${id}`);
} else {
history.push(`/sqllab?savedQueryId=${id}`);
}
};

const copyQueryLink = useCallback(
(id: number) => {
copyTextToClipboard(() =>
Promise.resolve(`${window.location.origin}/sqllab?savedQueryId=${id}`),
)
.then(() => {
addSuccessToast(t('Link Copied!'));
})
.catch(() => {
addDangerToast(t('Sorry, your browser does not support copying.'));
async (savedQuery: SavedQueryObject) => {
try {
const payload = {
dbId: savedQuery.db_id,
name: savedQuery.label,
schema: savedQuery.schema,
sql: savedQuery.sql,
autorun: false,
templateParams: null,
};

const response = await SupersetClient.post({
endpoint: '/api/v1/sqllab/permalink',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});

const { key } = response.json;
const permalink = `${window.location.origin}/sqllab/p/${key}`;

await navigator.clipboard.writeText(permalink);
addSuccessToast(t('Link Copied!'));
} catch (error) {
console.error('Error generating permalink:', error);
addDangerToast(t('There was an error generating the permalink.'));
}
},
[addDangerToast, addSuccessToast],
);
Expand Down Expand Up @@ -393,7 +401,7 @@ function SavedQueryList({
};
const handleEdit = ({ metaKey }: MouseEvent) =>
openInSqlLab(original.id, Boolean(metaKey));
const handleCopy = () => copyQueryLink(original.id);
const handleCopy = () => copyQueryLink(original);
const handleExport = () => handleBulkSavedQueryExport([original]);
const handleDelete = () => setQueryCurrentlyDeleting(original);

Expand Down
96 changes: 0 additions & 96 deletions superset/queries/saved_queries/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,8 @@
from flask import g, request, Response, send_file
from flask_appbuilder.api import expose, protect, rison, safe
from flask_appbuilder.models.sqla.interface import SQLAInterface
from flask_appbuilder.security.decorators import has_access_api
from flask_babel import ngettext
from sqlalchemy.exc import SQLAlchemyError

from superset import db, security_manager
from superset.commands.importers.exceptions import (
IncorrectFormatError,
NoValidFilesFoundError,
Expand Down Expand Up @@ -202,99 +199,6 @@ def pre_update(self, item: SavedQuery) -> None:
class SavedQueryRestApi(BaseSupersetModelRestApi):
datamodel = SQLAInterface(SavedQuery)

@has_access_api
@expose("/<int:pk>", methods=["GET"])
@protect()
@safe
@statsd_metrics
def get(self, pk: int, **kwargs: Any) -> Response:
"""
Retrieve a specific saved query.
---
get:
summary: Retrieve a specific saved query
description: >
Fetches the details of a saved query based on its primary key (ID).
The endpoint ensures that only the query owner or users with the
appropriate permissions ('can_read') can access the saved query.
parameters:
- in: path
name: pk
required: true
schema:
type: integer
description: The primary key (ID) of the saved query to retrieve.
responses:
200:
description: Details of the saved query
content:
application/json:
schema:
type: object
properties:
database:
type: object
description: The database associated with the query.
result:
type: object
description: Details of the saved query.
401:
$ref: '#/components/responses/401'
403:
$ref: '#/components/responses/403'
404:
$ref: '#/components/responses/404'
500:
$ref: '#/components/responses/500'
"""
try:
saved_query = db.session.query(SavedQuery).get(pk)
if not saved_query:
return self.response_404()

is_owner = saved_query.user_id == g.user.id
has_access = security_manager.can_access("can_read", "SavedQuery")
if not is_owner and not has_access:
return self.response_403()

response_data = {
"database": {
"id": saved_query.db_id,
"database_name": saved_query.database.database_name
if saved_query.database
else None,
},
"result": {
"id": saved_query.id,
"changed_on": saved_query.changed_on.isoformat()
if saved_query.changed_on
else None,
"database": {
"id": saved_query.db_id,
"database_name": saved_query.database.database_name
if saved_query.database
else None,
},
"sql": saved_query.sql,
"label": saved_query.label,
"schema": saved_query.schema,
"description": saved_query.description,
**({"rows": saved_query.rows} if saved_query.rows else {}),
**(
{"last_run": saved_query.last_run.isoformat()}
if saved_query.last_run
else {}
),
},
}

return self.response(200, **response_data)

except SQLAlchemyError as ex:
logger.error("Error fetching saved query %s: %s", pk, ex)
return self.response_500()

@expose("/", methods=("DELETE",))
@protect()
@safe
Expand Down

0 comments on commit a361eda

Please sign in to comment.