Skip to content

Commit

Permalink
Merge pull request #4898 from systeminit/brit/eng-2820-scaffold-apis-…
Browse files Browse the repository at this point in the history
…for-change-set-approvals

Adds V2 routes for change set approval and apply
  • Loading branch information
stack72 authored Oct 31, 2024
2 parents aa21ae6 + d076bca commit 20ba967
Show file tree
Hide file tree
Showing 27 changed files with 1,133 additions and 99 deletions.
10 changes: 10 additions & 0 deletions app/web/src/api/sdf/dal/workspace.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { ChangeSetId, ChangeSet } from "./change_set";

export interface Workspace {
pk: string;
name: string;
created_at: IsoDateString;
updated_at: IsoDateString;
token: string;
}

export interface WorkspaceMetadata {
name: string;
id: string;
default_change_set_id: ChangeSetId;
change_sets: ChangeSet[];
approvers: string[];
}
95 changes: 94 additions & 1 deletion app/web/src/store/change_sets.store.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { defineStore } from "pinia";
import * as _ from "lodash-es";
import { watch } from "vue";
import { ApiRequest, addStoreHooks } from "@si/vue-lib/pinia";
import { ApiRequest, addStoreHooks, URLPattern } from "@si/vue-lib/pinia";
import { useToast } from "vue-toastification";
import {
ChangeSet,
ChangeSetId,
ChangeSetStatus,
} from "@/api/sdf/dal/change_set";
import { WorkspaceMetadata } from "@/api/sdf/dal/workspace";
import router from "@/router";
import { UserId } from "@/store/auth.store";
import IncomingChangesMerging from "@/components/toasts/IncomingChangesMerging.vue";
Expand Down Expand Up @@ -35,6 +36,12 @@ export interface OpenChangeSetsView {
export function useChangeSetsStore() {
const workspacesStore = useWorkspacesStore();
const workspacePk = workspacesStore.selectedWorkspacePk;
const BASE_API = [
"v2",
"workspaces",
{ workspacePk },
"change-sets",
] as URLPattern;

return addStoreHooks(
workspacePk,
Expand Down Expand Up @@ -226,6 +233,92 @@ export function useChangeSetsStore() {
},
});
},
async FORCE_APPLY_CHANGE_SET(username: string) {
if (!this.selectedChangeSet) throw new Error("Select a change set");
const selectedChangeSetId = this.selectedChangeSetId;
return new ApiRequest<{ changeSet: ChangeSet }>({
method: "post",
url: BASE_API.concat([{ selectedChangeSetId }, "force_apply"]),
// todo(brit): decide what needs to happen here
optimistic: () => {
toast({
component: IncomingChangesMerging,
props: {
username,
},
});
},
_delay: 2000,
onSuccess: (response) => {
// this.changeSetsById[response.changeSet.id] = response.changeSet;
},
onFail: () => {
// todo: show something!
},
});
},
async REQUEST_CHANGE_SET_APPROVAL() {
if (!this.selectedChangeSet) throw new Error("Select a change set");
const selectedChangeSetId = this.selectedChangeSetId;
return new ApiRequest({
method: "post",
url: BASE_API.concat([{ selectedChangeSetId }, "request_approval"]),
});
},
async APPROVE_CHANGE_SET_FOR_APPLY() {
if (!this.selectedChangeSet) throw new Error("Select a change set");
const selectedChangeSetId = this.selectedChangeSetId;
return new ApiRequest({
method: "post",
url: BASE_API.concat([{ selectedChangeSetId }, "approve"]),
});
},
async REJECT_CHANGE_SET_APPLY() {
if (!this.selectedChangeSet) throw new Error("Select a change set");
const selectedChangeSetId = this.selectedChangeSetId;
return new ApiRequest({
method: "post",
url: BASE_API.concat([{ selectedChangeSetId }, "reject"]),
});
},
async CANCEL_APPROVAL_REQUEST() {
if (!this.selectedChangeSet) throw new Error("Select a change set");
const selectedChangeSetId = this.selectedChangeSetId;
return new ApiRequest({
method: "post",
url: BASE_API.concat([
{ selectedChangeSetId },
"cancel_approval_request",
]),
});
},
async REOPEN_CHANGE_SET() {
if (!this.selectedChangeSet) throw new Error("Select a change set");
const selectedChangeSetId = this.selectedChangeSetId;
return new ApiRequest({
method: "post",
url: BASE_API.concat([{ selectedChangeSetId }, "reopen"]),
});
},
async APPLY_CHANGE_SET_V2() {
if (!this.selectedChangeSet) throw new Error("Select a change set");
const selectedChangeSetId = this.selectedChangeSetId;
return new ApiRequest({
method: "post",
url: BASE_API.concat([{ selectedChangeSetId }, "apply"]),
});
},
async FETCH_CHANGE_SETS_V2() {
if (!this.selectedChangeSet) throw new Error("Select a change set");
return new ApiRequest<WorkspaceMetadata>({
method: "get",
url: BASE_API.concat(["list"]),
onSuccess: (response) => {
this.headChangeSetId = response.default_change_set_id;
this.changeSetsById = _.keyBy(response.change_sets, "id");
},
});
},
async APPLY_CHANGE_SET_VOTE(vote: string) {
if (!this.selectedChangeSet) throw new Error("Select a change set");
return new ApiRequest({
Expand Down
70 changes: 47 additions & 23 deletions lib/dal-test/src/helpers/change_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,34 +56,28 @@ impl ChangeSetTestHelpers {
))
}

/// Applies the current [`ChangeSet`] to its base [`ChangeSet`]. Then, it updates the snapshot
/// to the visibility without using an editing [`ChangeSet`]. In other words, the resulting,
/// snapshot is "HEAD" without an editing [`ChangeSet`].
/// Also locks existing editing funcs and schema variants to mimic SDF
pub async fn apply_change_set_to_base(ctx: &mut DalContext) -> Result<()> {
// Lock all unlocked variants
for schema_id in Schema::list_ids(ctx).await? {
let schema = Schema::get_by_id_or_error(ctx, schema_id).await?;
let Some(variant) = SchemaVariant::get_unlocked_for_schema(ctx, schema_id).await?
else {
continue;
};
/// Apply Changeset To base Approvals
pub async fn apply_change_set_to_base_approvals(ctx: &mut DalContext) -> Result<()> {
ChangeSet::prepare_for_apply(ctx).await?;

let variant_id = variant.id();
Self::commit_and_update_snapshot_to_visibility(ctx).await?;

variant.lock(ctx).await?;
schema.set_default_schema_variant(ctx, variant_id).await?;
}
// Lock all unlocked functions too
for func in Func::list_for_default_and_editing(ctx).await? {
if !func.is_locked {
func.lock(ctx).await?;
}
}
Self::apply_change_set_to_base_inner(ctx).await?;
Ok(())
}

/// Force Apply Changeset To base Approvals
pub async fn force_apply_change_set_to_base_approvals(ctx: &mut DalContext) -> Result<()> {
ChangeSet::prepare_for_force_apply(ctx).await?;

Self::commit_and_update_snapshot_to_visibility(ctx).await?;

let mut open_change_sets = ChangeSet::list_open(ctx)
Self::apply_change_set_to_base_inner(ctx).await?;
Ok(())
}

async fn apply_change_set_to_base_inner(ctx: &mut DalContext) -> Result<()> {
let mut open_change_sets = ChangeSet::list_active(ctx)
.await?
.iter()
.map(|change_set| (change_set.id, change_set.updated_at))
Expand Down Expand Up @@ -137,6 +131,36 @@ impl ChangeSetTestHelpers {
Ok(())
}

/// Applies the current [`ChangeSet`] to its base [`ChangeSet`]. Then, it updates the snapshot
/// to the visibility without using an editing [`ChangeSet`]. In other words, the resulting,
/// snapshot is "HEAD" without an editing [`ChangeSet`].
/// Also locks existing editing funcs and schema variants to mimic SDF
pub async fn apply_change_set_to_base(ctx: &mut DalContext) -> Result<()> {
// Lock all unlocked variants
for schema_id in Schema::list_ids(ctx).await? {
let schema = Schema::get_by_id_or_error(ctx, schema_id).await?;
let Some(variant) = SchemaVariant::get_unlocked_for_schema(ctx, schema_id).await?
else {
continue;
};

let variant_id = variant.id();

variant.lock(ctx).await?;
schema.set_default_schema_variant(ctx, variant_id).await?;
}
// Lock all unlocked functions too
for func in Func::list_for_default_and_editing(ctx).await? {
if !func.is_locked {
func.lock(ctx).await?;
}
}

Self::commit_and_update_snapshot_to_visibility(ctx).await?;
Self::apply_change_set_to_base_inner(ctx).await?;
Ok(())
}

/// Abandons the current [`ChangeSet`].
pub async fn abandon_change_set(ctx: &mut DalContext) -> Result<()> {
let mut abandonment_change_set = ChangeSet::find(ctx, ctx.change_set_id())
Expand Down
Loading

0 comments on commit 20ba967

Please sign in to comment.