Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shareableviz extended support #1749

Merged
merged 7 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ Please follow the established format:
- Use present tense (e.g. 'Add new feature')
- Include the ID number for the related PR (or PRs) in parentheses
-->
# Upcoming Release

## Major features and improvements

- Extend support for 'Publishing and sharing Kedro-viz' to Azure and GCP. (#1708, #1711)

# Upcoming release

Expand Down
4 changes: 2 additions & 2 deletions cypress/fixtures/mock/deploySuccessResponse.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"message": "Website deployed on S3",
"url": "http://myBucketName.s3-website.us-east-1.amazonaws.com"
"message": "Website deployed on AWS",
"url": "http://www.example.com"
}
59 changes: 41 additions & 18 deletions cypress/tests/ui/flowchart/shareable-urls.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('Shareable URLs', () => {
// Action
cy.reload();
cy.get('.pipeline-menu-button--deploy').click({ force: true });
cy.get('[data-test="disclaimerButton"]').click({ force: true });

// Assert after action
cy.get('.shareable-url-modal .modal__wrapper').contains(
Expand All @@ -28,6 +29,7 @@ describe('Shareable URLs', () => {
// Action
cy.reload();
cy.get('.pipeline-menu-button--deploy').click({ force: true });
cy.get('[data-test="disclaimerButton"]').click({ force: true });

// Assert after action
cy.get('.shareable-url-modal .modal__wrapper').contains(
Expand All @@ -38,6 +40,7 @@ describe('Shareable URLs', () => {
it('verifies that shareable url modal closes on close button click #TC-54', () => {
// Action
cy.get('.pipeline-menu-button--deploy').click();
cy.get('[data-test="disclaimerButton"]').click();
cy.get('.shareable-url-modal__button-wrapper button')
.contains('Cancel')
.click();
Expand All @@ -49,65 +52,73 @@ describe('Shareable URLs', () => {
);
});

it('verifies that users can click on region dropdown and see all region options #TC-55', () => {
const regionCount = 30;
it('verifies that users can click on platform dropdown and see all platform options #TC-55', () => {
const platformCount = 3;

// Action
cy.get('.pipeline-menu-button--deploy').click();
cy.get('[data-test="disclaimerButton"]').click();
cy.get('.shareable-url-modal [data-test=kedro-pipeline-selector]').click();

// Assert after action
cy.get('.shareable-url-modal .menu-option').should(
'have.length',
regionCount
platformCount
);
});

it('verifies that publish button should be disabled when region is not selected and bucket name is empty #TC-56', () => {
const selectedRegion = 'Select a region';
it('verifies that publish button should be disabled when platform is not selected and bucket name & endpoint name are empty #TC-56', () => {
const selectedPlatform = 'Select a hosting platform';
const primaryButtonNodeText = 'Publish';

// Action
cy.get('.pipeline-menu-button--deploy').click();
cy.get('[data-test="disclaimerButton"]').click();

// Assert after action
cy.get(
'.shareable-url-modal [data-test=kedro-pipeline-selector] .dropdown__label span'
).contains(selectedRegion);
cy.get('.shareable-url-modal textarea').should('have.value', '');
).contains(selectedPlatform);
cy.get('.shareable-url-modal input').should('have.value', '');
cy.get('.shareable-url-modal__button-wrapper button')
.contains(primaryButtonNodeText)
.should('be.disabled');
});

it('verifies that publish button should be disabled when a bucket region is selected and bucket name is empty #TC-57', () => {
it('verifies that publish button should be disabled when a platform is selected and bucket name is empty #TC-57', () => {
const primaryButtonNodeText = 'Publish';

// Action
cy.get('.pipeline-menu-button--deploy').click();
cy.get('[data-test="disclaimerButton"]').click();
cy.get('.shareable-url-modal [data-test=kedro-pipeline-selector]').click();
cy.get('.shareable-url-modal .dropdown__options section div')
.first()
.click();

// Assert after action
cy.get('.shareable-url-modal textarea').should('have.value', '');
cy.get('.shareable-url-modal input').should('have.value', '');
cy.get('.shareable-url-modal__button-wrapper button')
.contains(primaryButtonNodeText)
.should('be.disabled');
});

it('verifies that publish button should be enabled when region is selected and bucket name is not empty #TC-58', () => {
it('verifies that publish button should be enabled when platform is selected and bucket name & endpoint name are not empty #TC-58', () => {
const endpointName = 'http://www.example.com';
const bucketName = 'myBucketName';
const primaryButtonNodeText = 'Publish';

// Action
cy.get('.pipeline-menu-button--deploy').click();
cy.get('[data-test="disclaimerButton"]').click();
cy.get('.shareable-url-modal [data-test=kedro-pipeline-selector]').click();
cy.get('.shareable-url-modal .dropdown__options section div')
.first()
.click();
cy.get('.shareable-url-modal textarea').type(bucketName);
cy.get('.shareable-url-modal [data-test="bucket_name"]').type(bucketName);
cy.get('.shareable-url-modal [data-test="endpoint_name"]').type(
endpointName
);

// Assert after action
cy.get('.shareable-url-modal__button-wrapper button')
Expand All @@ -116,17 +127,20 @@ describe('Shareable URLs', () => {
});

it('verifies that error message appears with wrong inputs on publish button click #TC-59', () => {
const endpointName = 'http://www.example.com';
const bucketName = 'myBucketName';
const primaryButtonNodeText = 'Publish';
const errorButtonNodeText = 'Go back';

// Action
cy.get('.pipeline-menu-button--deploy').click();
cy.get('[data-test="disclaimerButton"]').click();
cy.get('.shareable-url-modal [data-test=kedro-pipeline-selector]').click();
cy.get('.shareable-url-modal .dropdown__options section div')
.first()
.click();
cy.get('.shareable-url-modal textarea').type(bucketName);
cy.get('.shareable-url-modal [data-test="bucket_name"]').type(bucketName);
cy.get('.shareable-url-modal [data-test="endpoint_name"]').type(
endpointName
);
cy.get('.shareable-url-modal__button-wrapper button')
.contains(primaryButtonNodeText)
.click();
Expand All @@ -135,11 +149,11 @@ describe('Shareable URLs', () => {
cy.get('.shareable-url-modal .modal__wrapper').contains(
'Something went wrong. Please try again later.'
);
cy.get('.shareable-url-modal__error button').contains(errorButtonNodeText);
});

it('verifies that AWS link is generated with correct inputs on publish button click #TC-60', () => {
const bucketName = 'myBucketName';
const endpointName = 'http://www.example.com';
const primaryButtonNodeText = 'Publish';

// Intercept the network request to mock with a fixture
Expand All @@ -152,11 +166,15 @@ describe('Shareable URLs', () => {
// Action
cy.reload();
cy.get('.pipeline-menu-button--deploy').click();
cy.get('[data-test="disclaimerButton"]').click();
cy.get('.shareable-url-modal [data-test=kedro-pipeline-selector]').click();
cy.get('.shareable-url-modal .dropdown__options section div')
.first()
.click();
cy.get('.shareable-url-modal textarea').type(bucketName);
cy.get('.shareable-url-modal [data-test="bucket_name"]').type(bucketName);
cy.get('.shareable-url-modal [data-test="endpoint_name"]').type(
endpointName
);
cy.get('.shareable-url-modal__button-wrapper button')
.contains(primaryButtonNodeText)
.click();
Expand All @@ -172,8 +190,9 @@ describe('Shareable URLs', () => {

it('verifies that AWS link is generated with correct inputs on Republish button click #TC-61', () => {
const bucketName = 'myBucketName';
const endpointName = 'http://www.example.com';
const primaryButtonNodeText = 'Publish';
const primaryButtonNodeTextVariant = 'Republish';
const primaryButtonNodeTextVariant = 'Publish';
const secondaryButtonNodeText = 'Link Settings';

// Intercept the network request to mock with a fixture
Expand All @@ -186,11 +205,15 @@ describe('Shareable URLs', () => {
// Action
cy.reload();
cy.get('.pipeline-menu-button--deploy').click();
cy.get('[data-test="disclaimerButton"]').click();
cy.get('.shareable-url-modal [data-test=kedro-pipeline-selector]').click();
cy.get('.shareable-url-modal .dropdown__options section div')
.first()
.click();
cy.get('.shareable-url-modal textarea').type(bucketName);
cy.get('.shareable-url-modal [data-test="bucket_name"]').type(bucketName);
cy.get('.shareable-url-modal [data-test="endpoint_name"]').type(
endpointName
);
cy.get('.shareable-url-modal__button-wrapper button')
.contains(primaryButtonNodeText)
.click();
Expand Down
8 changes: 5 additions & 3 deletions package/kedro_viz/api/rest/requests.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""`kedro_viz.api.rest.requests` defines REST request types."""

from pydantic import BaseModel


class S3DeployerConfiguration(BaseModel):
"""Credentials for S3 Deployer."""
class DeployerConfiguration(BaseModel):
"""Credentials for Deployers."""

region: str
platform: str
endpoint: str
bucket_name: str
11 changes: 3 additions & 8 deletions package/kedro_viz/api/rest/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
import logging
from typing import Any, Dict, List, Optional, Union

import fsspec
import orjson
import packaging
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse, ORJSONResponse
from kedro.io.core import get_protocol_and_path
from pydantic import BaseModel, ConfigDict

from kedro_viz.api.rest.utils import get_package_version
Expand Down Expand Up @@ -443,22 +441,19 @@ def save_api_pipeline_response_to_fs(pipelines_path: str, remote_fs: Any):
raise exc


def save_api_responses_to_fs(api_dir: str):
def save_api_responses_to_fs(path: str, remote_fs: Any):
"""Saves all Kedro Viz API responses to a directory."""
try:
protocol, path = get_protocol_and_path(str(api_dir))
remote_fs = fsspec.filesystem(protocol)

logger.debug(
"""Saving/Uploading api files to %s""",
api_dir,
path,
)

main_path = f"{path}/api/main"
nodes_path = f"{path}/api/nodes"
pipelines_path = f"{path}/api/pipelines"

if protocol == "file":
if "file" in remote_fs.protocol:
remote_fs.makedirs(path, exist_ok=True)
remote_fs.makedirs(nodes_path, exist_ok=True)
remote_fs.makedirs(pipelines_path, exist_ok=True)
Expand Down
34 changes: 28 additions & 6 deletions package/kedro_viz/api/rest/router.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""`kedro_viz.api.rest.router` defines REST routes and handling logic."""

# pylint: disable=missing-function-docstring, broad-exception-caught
import logging

from fastapi import APIRouter
from fastapi.responses import JSONResponse

from kedro_viz.api.rest.requests import S3DeployerConfiguration
from kedro_viz.integrations.deployment.s3_deployer import S3Deployer
from kedro_viz.api.rest.requests import DeployerConfiguration
from kedro_viz.integrations.deployment.deployer_factory import DeployerFactory

from .responses import (
APIErrorMessage,
Expand All @@ -19,6 +20,11 @@
get_selected_pipeline_response,
)

try:
from azure.core.exceptions import ServiceRequestError
except ImportError: # pragma: no cover
ServiceRequestError = None # type: ignore

logger = logging.getLogger(__name__)

router = APIRouter(
Expand Down Expand Up @@ -50,17 +56,33 @@ async def get_single_pipeline_data(registered_pipeline_id: str):


@router.post("/deploy")
async def deploy_kedro_viz(input_values: S3DeployerConfiguration):
async def deploy_kedro_viz(input_values: DeployerConfiguration):
try:
deployer = S3Deployer(input_values.region, input_values.bucket_name)
url = deployer.deploy_and_get_url()
response = {"message": "Website deployed on S3", "url": url}
deployer = DeployerFactory.create_deployer(
input_values.platform, input_values.endpoint, input_values.bucket_name
)
deployer.deploy()
response = {
"message": "Website deployed on "
f"{input_values.platform and input_values.platform.upper()}",
"url": input_values.endpoint,
}
return JSONResponse(status_code=200, content=response)
except PermissionError as exc: # pragma: no cover
logger.exception("Permission error in deploying Kedro Viz : %s ", exc)
return JSONResponse(
status_code=401, content={"message": "Please provide valid credentials"}
)
except (
# pylint: disable=catching-non-exception
(FileNotFoundError, ServiceRequestError)
if ServiceRequestError is not None
else FileNotFoundError
) as exc: # pragma: no cover
logger.exception("FileNotFoundError while deploying Kedro Viz : %s ", exc)
return JSONResponse(
status_code=400, content={"message": "The specified bucket does not exist"}
)
except Exception as exc: # pragma: no cover
logger.exception("Deploying Kedro Viz failed: %s ", exc)
return JSONResponse(status_code=500, content={"message": f"{exc}"})
Expand Down
36 changes: 3 additions & 33 deletions package/kedro_viz/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""`kedro_viz.constants` defines constants to be used throughout the application."""

import kedro
from packaging.version import parse

Expand All @@ -9,37 +10,6 @@
DEFAULT_HOST = "127.0.0.1"
DEFAULT_PORT = 4141

VIZ_DEPLOY_TIME_LIMIT = 60
VIZ_DEPLOY_TIME_LIMIT = 300

AWS_REGIONS = [
"us-east-2",
"us-east-1",
"us-west-1",
"us-west-2",
"af-south-1",
"ap-east-1",
"ap-south-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-south-1",
"ap-northeast-3",
"ap-northeast-2",
"ap-southeast-1",
"ap-southeast-2",
"ap-northeast-1",
"ca-central-1",
"cn-north-1",
"cn-northwest-1",
"eu-central-1",
"eu-west-1",
"eu-west-2",
"eu-south-1",
"eu-west-3",
"eu-north-1",
"eu-south-2",
"eu-central-2",
"sa-east-1",
"me-south-1",
"me-central-1",
"il-central-1",
]
SHAREABLEVIZ_SUPPORTED_PLATFORMS = ["aws", "azure", "gcp"]
Loading