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

Get resource config pointers using WASM #1414

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
5 changes: 4 additions & 1 deletion .licensee.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
"spdx-ranges": "2.1.1",

"// Public Domain": "0.0.0",
"jsonify": "0.0.1"
"jsonify": "0.0.1",

"// BSL": "0.0.0",
"flow-web": "0.4.0"
},
"ignore": [{ "prefix": "@estuary" }]
}
38 changes: 38 additions & 0 deletions deps/flow-web/hack-in-local-copy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash

# Check if exactly one argument is passed
if [ $# -ne 1 ]; then
echo "Please provide the path to flow (ex: /home/user/repos/flow)"
exit 1
fi

# The directory path from the argument
SOURCE_DIR="$1/crates/flow-web/pkg/"

# Check if the directory exists
if [ ! -d "$SOURCE_DIR" ]; then
echo "Error: '$SOURCE_DIR' is not a valid directory."
exit 1
fi

# Destination directory
DEST_DIR="node_modules/@estuary/flow-web"

# Create the destination directory if it doesn't exist
mkdir -p "$DEST_DIR"

# Copy the directory to the destination
cp -r "$SOURCE_DIR"/* "$DEST_DIR"

echo "Directory '$SOURCE_DIR' has been copied to '$DEST_DIR'."

# Path to the directory to delete
DIR_TO_DELETE="node_modules/.vite/deps"

# Check if the directory exists, and delete it if it does
if [ -d "$DIR_TO_DELETE" ]; then
rm -r "$DIR_TO_DELETE"
echo "Directory '$DIR_TO_DELETE' has been deleted."
else
echo "Directory '$DIR_TO_DELETE' does not exist."
fi
3 changes: 3 additions & 0 deletions deps/flow/hack-in-flow-web.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@


node_modules/%40estuary/flow-web
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"installDataPlane": "npm install 'https://gitpkg.now.sh/estuary/data-plane-gateway/client/dist?main'",
"beta_logRocketIntegrity": "node --env-file .env ./scripts/logRocketIntegirty",
"generate-flow-types": "cd deps/flow && ./fetch-flow.sh && ./flow-bin/flowctl json-schema | ./flow-bin/flowctl schemalate typescript --name Catalog --hoist-definitions > flow.d.ts",
"generate-supabase-types": "node ./scripts/generateSupabaseTypes"
"generate-supabase-types": "node ./scripts/generateSupabaseTypes",
"hack-in-local-web-flow": "./deps/flow-web/hack-in-local-copy.sh"
},
"dependencies": {
"@date-fns/utc": "^1.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { Button } from '@mui/material';
import { AddCollectionDialogCTAProps } from 'components/shared/Entity/types';
import invariableStores from 'context/Zustand/invariableStores';
import { FormattedMessage } from 'react-intl';
import { useBinding_prefillResourceConfigs } from 'stores/Binding/hooks';
import { useBindingStore } from 'stores/Binding/Store';
import {
useBinding_prefillResourceConfigs,
useBinding_sourceCaptureFlags,
} from 'stores/Binding/hooks';
import { useSourceCaptureStore } from 'stores/SourceCapture/Store';
import { SourceCaptureDef } from 'types';
import { useStore } from 'zustand';
Expand All @@ -19,13 +21,10 @@ function AddSourceCaptureToSpecButton({ toggle }: AddCollectionDialogCTAProps) {

const { existingSourceCapture, updateDraft } = useSourceCapture();

const [
const {
sourceCaptureDeltaUpdatesSupported,
sourceCaptureTargetSchemaSupported,
] = useBindingStore((state) => [
state.sourceCaptureDeltaUpdatesSupported,
state.sourceCaptureTargetSchemaSupported,
]);
} = useBinding_sourceCaptureFlags();

const [sourceCapture, setSourceCapture, deltaUpdates, targetSchema] =
useSourceCaptureStore((state) => [
Expand Down Expand Up @@ -79,7 +78,11 @@ function AddSourceCaptureToSpecButton({ toggle }: AddCollectionDialogCTAProps) {
setSourceCapture(updatedSourceCapture.capture);

if (selectedRow?.writes_to) {
prefillResourceConfigs(selectedRow.writes_to, true);
prefillResourceConfigs(
selectedRow.writes_to,
true,
updatedSourceCapture
);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { useBindingStore } from 'stores/Binding/Store';
import { useBinding_sourceCaptureFlags } from 'stores/Binding/hooks';
import { useSourceCaptureStore_sourceCaptureDefinition } from 'stores/SourceCapture/hooks';
import SourceCaptureChipOption from './SourceCaptureChipOption';

function SourceCaptureChipOptionalSettings() {
const sourceCaptureDefinition =
useSourceCaptureStore_sourceCaptureDefinition();

const [
const {
sourceCaptureDeltaUpdatesSupported,
sourceCaptureTargetSchemaSupported,
] = useBindingStore((state) => [
state.sourceCaptureDeltaUpdatesSupported,
state.sourceCaptureTargetSchemaSupported,
]);
} = useBinding_sourceCaptureFlags();

if (
(sourceCaptureDeltaUpdatesSupported ||
Expand Down
9 changes: 3 additions & 6 deletions src/components/shared/Entity/AddDialog/OptionalSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ import { Stack, Typography } from '@mui/material';
import DeltaUpdates from 'components/editor/Bindings/DeltaUpdates';
import SchemaMode from 'components/editor/Bindings/SchemaMode';
import { useIntl } from 'react-intl';
import { useBindingStore } from 'stores/Binding/Store';
import { useBinding_sourceCaptureFlags } from 'stores/Binding/hooks';

function OptionalSettings() {
const intl = useIntl();

const [
const {
sourceCaptureDeltaUpdatesSupported,
sourceCaptureTargetSchemaSupported,
] = useBindingStore((state) => [
state.sourceCaptureDeltaUpdatesSupported,
state.sourceCaptureTargetSchemaSupported,
]);
} = useBinding_sourceCaptureFlags();

if (
!sourceCaptureDeltaUpdatesSupported &&
Expand Down
116 changes: 78 additions & 38 deletions src/services/ajv.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import {
get_resource_config_pointers,

Check failure on line 2 in src/services/ajv.ts

View workflow job for this annotation

GitHub Actions / Check Quality

Module '"@estuary/flow-web"' has no exported member 'get_resource_config_pointers'.
update_materialization_resource_spec,

Check failure on line 3 in src/services/ajv.ts

View workflow job for this annotation

GitHub Actions / Check Quality

Module '"@estuary/flow-web"' has no exported member 'update_materialization_resource_spec'.
} from '@estuary/flow-web';
import { createAjv } from '@jsonforms/core';
import { isEmpty } from 'lodash';
import { DefaultAjvResponse, Schema, SourceCaptureDef } from 'types';
import { Annotations } from 'types/jsonforms';
import { stripPathing } from 'utils/misc-utils';

Expand Down Expand Up @@ -82,17 +87,21 @@
}
}

export function createJSONFormDefaults(jsonSchema: any): DefaultAjvResponse;
export function createJSONFormDefaults(
jsonSchema: any,
collection?: string
): {
data: any;
errors: any[];
} {
collection: string,
dataDefaults: Schema
): DefaultAjvResponse;
export function createJSONFormDefaults(
jsonSchema: any,
collection?: string,
dataDefaults?: Schema
): DefaultAjvResponse {
// We start with an empty object, and then validate it to set any default values.
// Note that this requires all parent properties to also specify a `default` in the json
// schema.
const data = {};
const data = dataDefaults ?? {};

// Get the schema we want.
// If collection then we want to generate a Resource Schema
Expand All @@ -114,42 +123,73 @@
return { data, errors };
}

// TODO (web flow wasm) This should be fetched with WASM code
// waiting on https://github.com/estuary/flow/issues/1760
// TODO (web flow wasm - source capture)
// Maybe we can get this type from wasm?
export interface ResourceConfigPointers {
[Annotations.defaultResourceConfigName]?: boolean;
[Annotations.targetSchema]?: boolean;
[Annotations.deltaUpdates]?: boolean;
['x_collection_name']: string | undefined;
['x_schema_name']: string | undefined;
['x_delta_updates']: string | undefined;
}

export const findKeysInObject = (
obj: Record<string, any>,
keysToFind: string[],
results: ResourceConfigPointers = {}
): ResourceConfigPointers => {
// Iterate over each key in the object
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];

// Check if the key is one of the keys we're searching for
if (keysToFind.includes(key)) {
results[key] = value;
}

// If the value is an object, recurse into it
if (value && typeof value === 'object') {
findKeysInObject(value, keysToFind, results);
}
}
export const prepareSourceCaptureForServer = (arg: SourceCaptureDef) => {
const response = {
...arg,
};

// These are optional locally and in the spec.
// But when calling WASM we want to make sure they're always there
if (!response.deltaUpdates) {
response.deltaUpdates = false;
}

return results;
if (!response.targetSchema) {
response.targetSchema = 'leaveEmpty';
}

return response;
};

export const getResourceConfigPointers = (
schema: any
): ResourceConfigPointers | null => {
try {
const response = get_resource_config_pointers(schema);

if (!response.pointers) {
return null;
}

return response.pointers;

// eslint-disable-next-line @typescript-eslint/no-implicit-any-catch
} catch (e: any) {
return null;
}
};

export const getResourceConfigPointers = (schema: any) =>
findKeysInObject(schema, [
Annotations.defaultResourceConfigName,
Annotations.targetSchema,
Annotations.deltaUpdates,
]);
export const generateMaterializationResourceSpec = (
sourceCapture: SourceCaptureDef,
resourceSpecPointers: ResourceConfigPointers,
collectionName: string
) => {
try {
// TODO (web flow wasm - source capture)
// We need to do some better error handling here
const response = update_materialization_resource_spec({
resourceSpecPointers,
collectionName,
resourceSpec: {},
sourceCapture: prepareSourceCaptureForServer(sourceCapture),
});

if (!response) {
return null;
}

return JSON.parse(response);

// eslint-disable-next-line @typescript-eslint/no-implicit-any-catch
} catch (e: any) {
return null;
}
};
Loading
Loading