From 33a289046cb7f3be064a8d7e5957ba73b46d5c61 Mon Sep 17 00:00:00 2001 From: Nick Gerace Date: Thu, 18 Jul 2024 16:22:31 -0400 Subject: [PATCH 1/2] Respect schema-to-asset relationship when contributing assets This commit contains various changes related to respecting the one-to-one, schema-to-asset relationship that's presently in place. Normally, this commit description would be more detailed, but here is a rundown of the changes: - Move module-related information in "app/web" to its own file in "api/sdf/dal/module.ts" (this is reflected in the frontend types crate) - Move module syncing to the module store - Convert ModuleExportModal to AssetContributeModal (the user domain language now understands _assets_ to be upgradeable, installable and contribute-able) - Ensure AssetContributeModal only operates on what is passed down to it (avoids unnecessary computation since AssetListPanel is going to figure out what assets have "canContribute" set to "true" anyway) - Reduce new contribution modal functionality to not have the ability to add or remove assets (I'm iffy on this one, but the "automatic") nature of preparing the request and the 1:1 nature of schemas-to-assets nudges me towards contracting and reducing failure modes in the interim... I'd add this with more time) - Replace "module/export_module" with "v2/module/contribute" (we should consider a longer-term vision here domain wise... maybe this is all part of the asset concept and sdf should speak in assets?) - Use 207 MULTI STATUS error for partial module contribution success when the dal is not at fault (e.g. 2 of 5 succeeded and the failures were only at the upload step) - Use 502 BAD GATEWAY for total module contribution failure, but the dal was not at fault (e.g. 0 of 5 succeeded, but only at the upload step) - Use 400 BAD REQUEST for malformed module contribution inpit - Undo accidental buck2 support sync deps script commented out code on main - Loosen "canContribute" constraints Signed-off-by: Nick Gerace --- app/web/src/api/sdf/dal/module.ts | 25 +++ app/web/src/api/sdf/dal/schema.ts | 13 -- app/web/src/components/AssetCard.vue | 50 ++++- .../src/components/AssetContributeModal.vue | 114 ++++++++++ app/web/src/components/AssetListItem.vue | 5 +- app/web/src/components/AssetListPanel.vue | 89 ++++---- app/web/src/components/CustomizeTabs.vue | 5 +- app/web/src/components/InstallAsset.vue | 2 +- app/web/src/components/InstallAssetsPanel.vue | 20 +- .../Workspace/WorkspaceCustomizeAssets.vue | 4 +- .../components/modules/ModuleExportModal.vue | 205 ------------------ app/web/src/store/asset.store.ts | 108 ++++----- app/web/src/store/module.store.ts | 60 +++-- app/web/src/store/workspaces.store.ts | 2 +- lib/dal/src/module.rs | 77 ++++++- lib/dal/src/pkg/export.rs | 55 +++-- lib/dal/src/schema/variant.rs | 22 +- lib/dal/tests/integration_test/module.rs | 36 ++- lib/sdf-server/src/server/service/module.rs | 4 +- .../server/service/module/export_module.rs | 139 ------------ .../src/server/service/v2/module.rs | 27 ++- .../server/service/v2/module/contribute.rs | 96 ++++++++ lib/si-frontend-types-rs/src/lib.rs | 6 +- .../src/{synced_modules.rs => module.rs} | 16 +- .../src/schema_variant.rs | 2 - support/buck2/buck2-sync-cargo-deps.py | 21 +- 26 files changed, 633 insertions(+), 570 deletions(-) create mode 100644 app/web/src/api/sdf/dal/module.ts create mode 100644 app/web/src/components/AssetContributeModal.vue delete mode 100644 app/web/src/components/modules/ModuleExportModal.vue delete mode 100644 lib/sdf-server/src/server/service/module/export_module.rs create mode 100644 lib/sdf-server/src/server/service/v2/module/contribute.rs rename lib/si-frontend-types-rs/src/{synced_modules.rs => module.rs} (50%) diff --git a/app/web/src/api/sdf/dal/module.ts b/app/web/src/api/sdf/dal/module.ts new file mode 100644 index 0000000000..db82254003 --- /dev/null +++ b/app/web/src/api/sdf/dal/module.ts @@ -0,0 +1,25 @@ +import { SchemaId } from "./schema"; + +export type ModuleId = string; + +export interface LatestModule { + id: ModuleId; + name: string; + description: string; + ownerUserId: string; + ownerDisplayName: string; + metadata: object; + latestHash: string; + latestHashCreatedAt: IsoDateString; + schemaId: SchemaId; +} + +export interface ModuleContributeRequest { + modules: ModuleContributeRequestItem[]; +} + +export interface ModuleContributeRequestItem { + name: string; + version: string; + schemaId: SchemaId; +} diff --git a/app/web/src/api/sdf/dal/schema.ts b/app/web/src/api/sdf/dal/schema.ts index 31d2f96784..ccfdacb029 100644 --- a/app/web/src/api/sdf/dal/schema.ts +++ b/app/web/src/api/sdf/dal/schema.ts @@ -70,19 +70,6 @@ export interface SchemaVariant { canContribute: boolean; } -export type ModuleId = string; -export interface Module { - id: ModuleId; - name: string; - description: string; - ownerUserId: string; - ownerDisplayName: string; - metadata: object; - latestHash: string; - latestHashCreatedAt: IsoDateString; - schemaId: SchemaId; -} - export const outputSocketsAndPropsFor = (schemaVariant: SchemaVariant) => { const socketOptions = schemaVariant.outputSockets.map((socket) => ({ label: `Output Socket: ${socket.name}`, diff --git a/app/web/src/components/AssetCard.vue b/app/web/src/components/AssetCard.vue index f9380bbd9b..d37181183e 100644 --- a/app/web/src/components/AssetCard.vue +++ b/app/web/src/components/AssetCard.vue @@ -32,11 +32,13 @@ + + + +

+ Thanks for contributing! We will review your contribution, and reach out + via email or on our + Discord Server + if you have any questions. +

+
@@ -110,6 +132,10 @@ import { getAssetIcon } from "@/store/components.store"; import { useModuleStore } from "@/store/module.store"; import IconButton from "./IconButton.vue"; import EditingPill from "./EditingPill.vue"; +import AssetContributeModal from "./AssetContributeModal.vue"; +import { Modal } from "@si/vue-lib/design-system"; +import { ModuleContributeRequest } from "@/api/sdf/dal/module"; +import { format as dateFormat, parseISO } from "date-fns"; const props = defineProps({ titleCard: { type: Boolean }, @@ -121,6 +147,26 @@ const moduleStore = useModuleStore(); const router = useRouter(); const { theme } = useTheme(); +const contributeAssetModalRef = + ref>(); +const contributeAssetSuccessModalRef = ref>(); + +const contributeAsset = () => contributeAssetModalRef.value?.open(); +const onContributeAsset = () => contributeAssetSuccessModalRef.value?.open(); + +const contributeRequest = computed((): ModuleContributeRequest => { + const modules = []; + if (asset.value) { + const version = dateFormat(Date.now(), "yyyyMMddkkmmss"); + modules.push({ + name: `${asset.value.schemaName} ${version}`, + version, + schemaId: asset.value.schemaId, + }); + } + return { modules }; +}); + const editingVersionDoesNotExist = computed( () => assetStore.unlockedVariantIdForId[asset.value?.schemaVariantId ?? ""] === @@ -139,7 +185,7 @@ const asset = computed( ); const canUpdate = computed( - () => !!assetStore.upgradeableModules[props.assetId], + () => !!moduleStore.upgradeableModules[props.assetId], ); const updateAsset = () => { @@ -148,7 +194,7 @@ const updateAsset = () => { throw new Error("cannot update asset: no asset selected"); } - const module = assetStore.upgradeableModules[schemaVariantId]; + const module = moduleStore.upgradeableModules[schemaVariantId]; if (!module) { throw new Error("cannot update asset: no upgradeable module for asset"); } diff --git a/app/web/src/components/AssetContributeModal.vue b/app/web/src/components/AssetContributeModal.vue new file mode 100644 index 0000000000..9340694f32 --- /dev/null +++ b/app/web/src/components/AssetContributeModal.vue @@ -0,0 +1,114 @@ + + + diff --git a/app/web/src/components/AssetListItem.vue b/app/web/src/components/AssetListItem.vue index 6bb7dd17b3..1fa4eed894 100644 --- a/app/web/src/components/AssetListItem.vue +++ b/app/web/src/components/AssetListItem.vue @@ -54,6 +54,7 @@ import { TreeNode, Icon } from "@si/vue-lib/design-system"; import clsx from "clsx"; import { SchemaVariant } from "@/api/sdf/dal/schema"; import { useAssetStore, schemaVariantDisplayName } from "@/store/asset.store"; +import { useModuleStore } from "@/store/module.store"; import EditingPill from "./EditingPill.vue"; const props = defineProps({ @@ -62,10 +63,12 @@ const props = defineProps({ }); const assetStore = useAssetStore(); +const moduleStore = useModuleStore(); + const { selectedSchemaVariants: selectedAssets } = storeToRefs(assetStore); const canUpdate = computed( - () => !!assetStore.upgradeableModules[props.a.schemaVariantId], + () => !!moduleStore.upgradeableModules[props.a.schemaVariantId], ); const onClick = (e: MouseEvent) => { diff --git a/app/web/src/components/AssetListPanel.vue b/app/web/src/components/AssetListPanel.vue index e94c1f5538..0c5c6582d4 100644 --- a/app/web/src/components/AssetListPanel.vue +++ b/app/web/src/components/AssetListPanel.vue @@ -3,7 +3,7 @@
- - +

Thanks for contributing! We will review your contribution, and reach out via email or on our @@ -148,14 +147,19 @@ import { PillCounter, } from "@si/vue-lib/design-system"; import { useRouter } from "vue-router"; +import { format as dateFormat, parseISO } from "date-fns"; import SiSearch, { Filter } from "@/components/SiSearch.vue"; +import { + ModuleContributeRequest, + ModuleContributeRequestItem, +} from "@/api/sdf/dal/module"; import { useAssetStore } from "@/store/asset.store"; import { SchemaVariant } from "@/api/sdf/dal/schema"; import { getAssetIcon } from "@/store/components.store"; import { useModuleStore } from "@/store/module.store"; import AssetNameModal from "./AssetNameModal.vue"; import AssetListItem from "./AssetListItem.vue"; -import ModuleExportModal from "./modules/ModuleExportModal.vue"; +import AssetContributeModal from "./AssetContributeModal.vue"; import SidebarSubpanelTitle from "./SidebarSubpanelTitle.vue"; import IconButton from "./IconButton.vue"; @@ -169,35 +173,13 @@ const createAssetReqStatus = assetStore.getRequestStatus("CREATE_VARIANT"); const loadAssetsReqStatus = assetStore.getRequestStatus( "LOAD_SCHEMA_VARIANT_LIST", ); -const loadModulesReqStatus = assetStore.getRequestStatus("LOAD_MODULES"); +const syncModulesReqStatus = moduleStore.getRequestStatus("SYNC"); -const contributeAssetModalRef = ref>(); -const exportSuccessModalRef = ref>(); +const contributeAssetModalRef = + ref>(); +const contributeAssetSuccessModalRef = ref>(); const newAssetModalRef = ref>(); -const contributeLoadingTexts = [ - "Engaging Photon Torpedos...", - "Reticulating Splines...", - "Revolutionizing DevOps...", - "Calibrating Hyperspace Matrix...", - "Syncing Neural Circuitry...", - "Optimizing Tachyon Weave...", - "Tuning Fractal Harmonics...", - "Reshuffling Multiverse Threads...", - "Harmonizing Subspace Arrays...", - "Modulating Cybernetic Matrices...", - "Configuring Exo-Geometric Arrays...", - "Initializing Flux Capacitors...", - "Balancing Subatomic Resonance...", - "Fine-tuning Quantum Entanglement...", - "Matrixing Hyperdimensional Grids...", - "Coalescing Esoteric Code...", - "Syncopating Quantum Flux...", - "Reformatting Reality Lattice...", - "Fine-tuning Temporal Flux...", - "Syncing Cosmic Harmonics...", -]; - const searchRef = ref>(); const searchString = ref(""); @@ -205,11 +187,26 @@ const onSearch = (search: string) => { searchString.value = search.trim().toLocaleLowerCase(); }; +const contributeRequest = computed((): ModuleContributeRequest => { + let modules = [] as ModuleContributeRequestItem[]; + const filteredSchemaVariants = _.filter( + assetStore.schemaVariants, + (schemaVariant) => schemaVariant.canContribute, + ); + const version = dateFormat(Date.now(), "yyyyMMddkkmmss"); + modules = _.map(filteredSchemaVariants, (filteredSchemaVariant) => ({ + name: `${filteredSchemaVariant.schemaName} ${version}`, + version, + schemaId: filteredSchemaVariant.schemaId, + })); + return { modules }; +}); + const canContribute = computed(() => assetList.value.some((a) => a.canContribute), ); const canUpdate = computed( - () => Object.keys(assetStore.upgradeableModules).length > 0, + () => Object.keys(moduleStore.upgradeableModules).length !== 0, ); const categorizedAssets = computed(() => @@ -263,7 +260,7 @@ const categoryColor = (category: string) => { return "#000"; }; -const loadModules = async () => assetStore.LOAD_MODULES(); +const syncModules = async () => moduleStore.SYNC(); const newAsset = async (newAssetName: string) => { const result = await assetStore.CREATE_VARIANT(newAssetName); @@ -277,7 +274,7 @@ const newAsset = async (newAssetName: string) => { }; const updateAllAssets = () => { - Object.values(assetStore.upgradeableModules).forEach((module) => { + Object.values(moduleStore.upgradeableModules).forEach((module) => { moduleStore.INSTALL_REMOTE_MODULE(module.id); }); router.replace({ @@ -286,12 +283,12 @@ const updateAllAssets = () => { }; const contributeAsset = () => contributeAssetModalRef.value?.open(); -const onExport = () => exportSuccessModalRef.value?.open(); +const onContributeAsset = () => contributeAssetSuccessModalRef.value?.open(); const filters = computed(() => [ assetList.value.filter((a) => a.canContribute), assetList.value.filter( - (a) => !!assetStore.upgradeableModules[a.schemaVariantId], + (a) => !!moduleStore.upgradeableModules[a.schemaVariantId], ), assetList.value.filter((a) => !a.isLocked), ]); diff --git a/app/web/src/components/CustomizeTabs.vue b/app/web/src/components/CustomizeTabs.vue index 21421dd3e6..643f6b38eb 100644 --- a/app/web/src/components/CustomizeTabs.vue +++ b/app/web/src/components/CustomizeTabs.vue @@ -11,7 +11,8 @@ @@ -31,11 +32,13 @@ import { PropType, ref } from "vue"; import { TabGroup, TabGroupItem, PillCounter } from "@si/vue-lib/design-system"; import { useFeatureFlagsStore } from "@/store/feature_flags.store"; import { useAssetStore } from "@/store/asset.store"; +import { useModuleStore } from "@/store/module.store"; const router = useRouter(); const route = useRoute(); const featureFlagsStore = useFeatureFlagsStore(); const assetStore = useAssetStore(); +const moduleStore = useModuleStore(); const group = ref>(); diff --git a/app/web/src/components/InstallAsset.vue b/app/web/src/components/InstallAsset.vue index 64772e8dfb..92321e5185 100644 --- a/app/web/src/components/InstallAsset.vue +++ b/app/web/src/components/InstallAsset.vue @@ -88,8 +88,8 @@ import { Icon, } from "@si/vue-lib/design-system"; import { useModuleStore, ModuleSpec } from "@/store/module.store"; -import { ModuleId } from "@/api/sdf/dal/schema"; import { nilId } from "@/utils/nilId"; +import { ModuleId } from "@/api/sdf/dal/module"; import TruncateWithTooltip from "./TruncateWithTooltip.vue"; import CodeEditor from "./CodeEditor.vue"; diff --git a/app/web/src/components/InstallAssetsPanel.vue b/app/web/src/components/InstallAssetsPanel.vue index 7957b25da0..be603ca1d4 100644 --- a/app/web/src/components/InstallAssetsPanel.vue +++ b/app/web/src/components/InstallAssetsPanel.vue @@ -1,8 +1,8 @@