Skip to content

Commit

Permalink
Merge branch 'eng-2712-m1-migrate-wf-edit-trigger-endpoints-to' into …
Browse files Browse the repository at this point in the history
…eng-2943-investigate-result-content-route-tests
  • Loading branch information
likawind committed May 17, 2023
2 parents e60ac09 + 18011cb commit d3a2669
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 65 deletions.
10 changes: 7 additions & 3 deletions integration_tests/backend/test_reads.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
import pytest
import requests
import utils
from aqueduct.constants.enums import RuntimeType
from aqueduct.models.response_models import (
GetArtifactResultResponse,
GetDagResponse,
GetDagResultResponse,
GetNodeArtifactResponse,
GetNodeOperatorResponse,
Expand Down Expand Up @@ -354,9 +356,11 @@ def test_endpoint_workflow_dags_get(self):
resp = resp.json()

assert len(resp) == 2
assert "id" in resp[0]
assert resp[0]["workflow_id"] == str(flow_id)
assert "created_at" in resp[0]
for dag_dict in resp:
dag = GetDagResponse(**dag_dict)
assert dag.workflow_id == flow_id
assert dag.created_at != ""
assert dag.engine_config.type == RuntimeType.AQUEDUCT

def test_endpoint_dag_results_get(self):
flow_id, n_runs = self.flows["flow_with_metrics_and_checks"]
Expand Down
9 changes: 8 additions & 1 deletion sdk/aqueduct/models/response_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
SerializationType,
)
from aqueduct.models.artifact import ArtifactMetadata
from aqueduct.models.dag import Metadata, RetentionPolicy, Schedule
from aqueduct.models.dag import EngineConfig, Metadata, RetentionPolicy, Schedule
from aqueduct.models.execution_state import ExecutionState
from aqueduct.models.operators import LoadSpec, Operator, OperatorSpec
from aqueduct.models.utils import human_readable_timestamp
Expand All @@ -23,6 +23,13 @@ class ArtifactResult(BaseModel):


# V2 Responses
class GetDagResponse(BaseModel):
id: uuid.UUID
workflow_id: uuid.UUID
created_at: str
engine_config: EngineConfig


class GetDagResultResponse(BaseModel):
"""Represents the result of a single workflow run.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ export const SnowflakeCard: React.FC<Props> = ({
let values = [config.account_identifier, config.database, config.username];

if (detailedView) {
labels = labels.concat(['Warehouse', 'Schema']);
values = values.concat([config.warehouse, config.schema]);
labels = labels.concat(['Warehouse']);
values = values.concat([config.warehouse]);

// Only show the Schema field if it was set.
if (config.schema) {
labels = labels.concat(['Schema']);
values = values.concat([config.schema]);
}

// Only show the Role field if it was set.
if (config.role) {
Expand Down
79 changes: 59 additions & 20 deletions src/ui/common/src/components/integrations/cards/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ type ResourceCardTextProps = {

const paddingBetweenLabelAndValue = 8; // in pixels

// Maximum number of rows to show before creating a new column.
const maxNumRows = 3;

function chunkList(list: string[], chunkSize: number): string[][] {
return Array.from(
{ length: Math.ceil(list.length / chunkSize) },
(_, index) => list.slice(index * chunkSize, index * chunkSize + chunkSize)
);
}

// ASSUMPTION: if using for the resource summary card, labels.length <= maxNumRows!
// The format is "Label: Value". The label width is set to the maximum of all the provided labels.
export const ResourceCardText: React.FC<ResourceCardTextProps> = ({
labels,
Expand All @@ -30,24 +41,52 @@ export const ResourceCardText: React.FC<ResourceCardTextProps> = ({
};
const labelWidthNum = Math.max(...labels.map(useLabelWidth));

return (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
{labels.map((_, index) => (
<Box key={index} sx={{ display: 'flex', flexWrap: 'nowrap' }}>
<TruncatedText
variant="body2"
sx={{ fontWeight: 300, width: `${labelWidthNum}px` }}
>
{labels[index]}
</TruncatedText>
<TruncatedText
variant="body2"
sx={{ width: `calc(100% - ${labelWidthNum}px)` }}
>
{values[index]}
</TruncatedText>
</Box>
))}
</Box>
);
// Chunk the fields into their respective columns.
const chunkedLabels = chunkList(labels, maxNumRows);
const chunkedValues = chunkList(values, maxNumRows);

const labelAndValueColumn = (labelsForColumn: string[], colIndex: number) => {
// Only set the left margin for columns after the first one.
return (
<Box
key={colIndex}
sx={{
display: 'flex',
flexDirection: 'column',
ml: colIndex > 0 ? 2 : 0,
}}
>
{labelsForColumn.map((_, rowIndex) => (
<Box key={rowIndex} sx={{ display: 'flex', flexWrap: 'nowrap' }}>
<TruncatedText
variant="body2"
sx={{ fontWeight: 300, width: `${labelWidthNum}px` }}
>
{labelsForColumn[rowIndex]}
</TruncatedText>
<TruncatedText
variant="body2"
sx={{ width: `calc(100% - ${labelWidthNum}px)` }}
>
{chunkedValues[colIndex][rowIndex]}
</TruncatedText>
</Box>
))}
</Box>
);
};

// We need to separate the multi-column case from the single-column one, since the latter is used in the
// resource summary card, and the additional row flow messes up the width calculation.
if (chunkedLabels.length > 1) {
return (
<Box sx={{ display: 'flex', flexDirection: 'row' }}>
{chunkedLabels.map((labelsForColumn, colIndex) =>
labelAndValueColumn(labelsForColumn, colIndex)
)}
</Box>
);
} else {
return labelAndValueColumn(chunkedLabels[0], 0);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,13 @@ const Placeholders: BigQueryConfig = {
export const BigQueryDialog: React.FC<IntegrationDialogProps> = ({
editMode = false,
}) => {
const { setValue, getValues } = useFormContext();

const [fileName, setFileName] = useState<string>(null);

const { setValue } = useFormContext();
const [fileData, setFileData] = useState<FileData | null>(null);
const setFile = (fileData: FileData | null) => {
setFileName(fileData?.name ?? '');
setValue('service_account_credentials', fileData?.data);
setFileData(fileData);
};

const fileData =
fileName && !!getValues('service_account_credentials')
? {
name: fileName,
data: getValues('service_account_credentials'),
}
: null;

const fileUploadDescription = (
<>
<>Follow the instructions </>
Expand Down Expand Up @@ -100,9 +90,15 @@ export function readCredentialsFile(

export function getBigQueryValidationSchema() {
return Yup.object().shape({
name: Yup.string().required('Please enter a name'),
project_id: Yup.string().required('Please enter a project ID'),
service_account_credentials: Yup.string().required(
'Please upload a service account key file'
),
service_account_credentials: Yup.string()
.transform((value) => {
if (!value?.data) {
return null;
}
return value.data;
})
.required('Please upload a service account key file'),
});
}
27 changes: 13 additions & 14 deletions src/ui/common/src/components/integrations/dialogs/gcsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,20 @@ export const GCSDialog: React.FC<GCSDialogProps> = ({
setMigrateStorage,
}) => {
// Setup for the checkbox component.
const { control, setValue, getValues } = useFormContext();
const { control, setValue } = useFormContext();
const [fileData, setFileData] = useState<FileData | null>(null);
const { field } = useController({
control,
name: 'use_as_storage',
defaultValue: 'true',
rules: { required: true },
});

const [fileName, setFileName] = useState<string>(null);
const setFile = (fileData: FileData | null) => {
setFileName(fileData?.name ?? null);
setValue('service_account_credentials', fileData?.data);
setFileData(fileData);
};

const fileData =
fileName && !!getValues('service_account_credentials')
? {
name: fileName,
data: getValues('service_account_credentials'),
}
: null;

const fileUploadDescription = (
<>
<>Follow the instructions </>
Expand Down Expand Up @@ -141,9 +133,16 @@ export function readCredentialsFile(

export function getGCSValidationSchema() {
return Yup.object().shape({
name: Yup.string().required('Please enter a name'),
bucket: Yup.string().required('Please enter a bucket name'),
service_account_credentials: Yup.string().required(
'Please upload a service account key file.'
),
service_account_credentials: Yup.string()
.transform((value) => {
if (!value?.data) {
return null;
}

return value.data;
})
.required('Please upload a service account key file.'),
});
}
38 changes: 31 additions & 7 deletions src/ui/common/src/components/integrations/dialogs/s3Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const S3Dialog: React.FC<S3DialogProps> = ({

const setFile = (fileData: FileData | null) => {
// Update the react-hook-form value
setValue('config_file_content', fileData?.data);
setValue('config_file_content', fileData);
// Set state to trigger re-render of file upload field.
setFileData(fileData);
};
Expand Down Expand Up @@ -199,6 +199,11 @@ export const S3Dialog: React.FC<S3DialogProps> = ({
value={currentTab}
onChange={(_, value) => {
setValue('type', value);
// reset config_file_profile when changing tabs.
setValue('config_file_profile', '', {
shouldDirty: false,
shouldTouch: false,
});
setCurrentTab(value);
}}
>
Expand Down Expand Up @@ -249,21 +254,40 @@ export function getS3ValidationSchema() {
access_key_id: Yup.string().when('type', {
is: 'access_key',
then: Yup.string().required('Please enter an access key id'),
otherwise: null,
}),
secret_access_key: Yup.string().when('type', {
is: 'access_key',
then: Yup.string().required('Please enter a secret access key'),
otherwise: null,
}),
secret_access_key: Yup.string().required(
'Please enter a secret access key'
),
config_file_path: Yup.string().when('type', {
is: 'config_file_path',
then: Yup.string().required('Please enter a profile path'),
otherwise: null,
}),
config_file_profile: Yup.string().when('type', {
is: 'config_file_path' || 'config_file_content',
is: (value) =>
value === 'config_file_path' || value === 'config_file_content',
then: Yup.string().required('Please enter a config file profile'),
otherwise: null,
}),
config_file_content: Yup.string().when('type', {
is: 'config_file_content',
then: Yup.string().required('Please upload a credentials file'),
is: (value) => value === 'config_file_content',
then: Yup.string()
.transform((value) => {
// Depending on if dragged and dropped or uploaded via file picker, we can get two different things.
if (typeof value === 'object') {
return value.data;
} else if (typeof value === 'string') {
const parsed = JSON.parse(value);
return parsed.data;
}

return value;
})
.required('Please upload a credentials file'),
otherwise: null,
}),
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export function getSnowflakeValidationSchema() {
),
warehouse: Yup.string().required('Please enter a warehouse'),
database: Yup.string().required('Please enter a database'),
schema: Yup.string(),
schema: Yup.string().transform((value) => value || 'public'),
username: Yup.string().required('Please enter a username'),
password: Yup.string().required('Please enter a password'),
role: Yup.string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ const IntegrationDetailsPage: React.FC<IntegrationDetailsPageProps> = ({
)}

{showResourceDetails && (
<Box sx={{ my: 1 }}>
<Box sx={{ my: 1, mt: 2 }}>
<ResourceFieldsDetailsCard
integration={selectedIntegration}
detailedView={true}
Expand Down

0 comments on commit d3a2669

Please sign in to comment.