Skip to content

Commit

Permalink
TASK: Trigger conflict resolution from within Publish saga
Browse files Browse the repository at this point in the history
  • Loading branch information
grebaldi committed Apr 25, 2024
1 parent a49fd0e commit 926004b
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 24 deletions.
30 changes: 30 additions & 0 deletions packages/neos-ui-redux-store/src/CR/Publishing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export enum PublishingScope {
export enum PublishingPhase {
START,
ONGOING,
CONFLICTS,
SUCCESS,
ERROR
}
Expand All @@ -35,6 +36,7 @@ export type State = null | {
process:
| { phase: PublishingPhase.START }
| { phase: PublishingPhase.ONGOING }
| { phase: PublishingPhase.CONFLICTS }
| {
phase: PublishingPhase.ERROR;
error: null | AnyError;
Expand All @@ -51,6 +53,8 @@ export enum actionTypes {
STARTED = '@neos/neos-ui/CR/Publishing/STARTED',
CANCELLED = '@neos/neos-ui/CR/Publishing/CANCELLED',
CONFIRMED = '@neos/neos-ui/CR/Publishing/CONFIRMED',
CONFLICTS_OCCURRED = '@neos/neos-ui/CR/Publishing/CONFLICTS_OCCURRED',
CONFLICTS_RESOLVED = '@neos/neos-ui/CR/Publishing/CONFLICTS_RESOLVED',
FAILED = '@neos/neos-ui/CR/Publishing/FAILED',
RETRIED = '@neos/neos-ui/CR/Publishing/RETRIED',
SUCEEDED = '@neos/neos-ui/CR/Publishing/SUCEEDED',
Expand All @@ -74,6 +78,16 @@ const cancel = () => createAction(actionTypes.CANCELLED);
*/
const confirm = () => createAction(actionTypes.CONFIRMED);

/**
* Signal that conflicts have occurred during the publish/discard operation
*/
const conflicts = () => createAction(actionTypes.CONFLICTS_OCCURRED);

/**
* Signal that conflicts have been resolved during the publish/discard operation
*/
const resolveConflicts = () => createAction(actionTypes.CONFLICTS_RESOLVED);

/**
* Signal that the ongoing publish/discard workflow has failed
*/
Expand Down Expand Up @@ -108,6 +122,8 @@ export const actions = {
start,
cancel,
confirm,
conflicts,
resolveConflicts,
fail,
retry,
succeed,
Expand Down Expand Up @@ -145,6 +161,20 @@ export const reducer = (state: State = defaultState, action: Action): State => {
phase: PublishingPhase.ONGOING
}
};
case actionTypes.CONFLICTS_OCCURRED:
return {
...state,
process: {
phase: PublishingPhase.CONFLICTS
}
};
case actionTypes.CONFLICTS_RESOLVED:
return {
...state,
process: {
phase: PublishingPhase.ONGOING
}
};
case actionTypes.FAILED:
return {
...state,
Expand Down
25 changes: 25 additions & 0 deletions packages/neos-ui-redux-store/src/CR/Syncing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type Conflict = {
};

export type State = null | {
autoAcknowledge: boolean;
process:
| { phase: SyncingPhase.START }
| { phase: SyncingPhase.ONGOING }
Expand Down Expand Up @@ -178,12 +179,24 @@ export const reducer = (state: State = defaultState, action: Action): State => {
if (state === null) {
if (action.type === actionTypes.STARTED) {
return {
autoAcknowledge: false,
process: {
phase: SyncingPhase.START
}
};
}

if (action.type === actionTypes.CONFLICTS_DETECTED) {
return {
autoAcknowledge: true,
process: {
phase: SyncingPhase.CONFLICT,
conflicts: action.payload.conflicts,
strategy: null
}
};
}

return null;
}

Expand All @@ -192,12 +205,14 @@ export const reducer = (state: State = defaultState, action: Action): State => {
return null;
case actionTypes.CONFIRMED:
return {
...state,
process: {
phase: SyncingPhase.ONGOING
}
};
case actionTypes.CONFLICTS_DETECTED:
return {
...state,
process: {
phase: SyncingPhase.CONFLICT,
conflicts: action.payload.conflicts,
Expand All @@ -207,6 +222,7 @@ export const reducer = (state: State = defaultState, action: Action): State => {
case actionTypes.RESOLUTION_STARTED:
if (state.process.phase === SyncingPhase.CONFLICT) {
return {
...state,
process: {
...state.process,
phase: SyncingPhase.RESOLVING,
Expand All @@ -218,6 +234,7 @@ export const reducer = (state: State = defaultState, action: Action): State => {
case actionTypes.RESOLUTION_CANCELLED:
if (state.process.phase === SyncingPhase.RESOLVING) {
return {
...state,
process: {
...state.process,
phase: SyncingPhase.CONFLICT
Expand All @@ -227,25 +244,33 @@ export const reducer = (state: State = defaultState, action: Action): State => {
return state;
case actionTypes.RESOLUTION_CONFIRMED:
return {
...state,
process: {
phase: SyncingPhase.ONGOING
}
};
case actionTypes.FAILED:
return {
...state,
process: {
phase: SyncingPhase.ERROR,
error: action.payload.error
}
};
case actionTypes.RETRIED:
return {
...state,
process: {
phase: SyncingPhase.ONGOING
}
};
case actionTypes.SUCEEDED:
if (state.autoAcknowledge) {
return null;
}

return {
...state,
process: {
phase: SyncingPhase.SUCCESS
}
Expand Down
52 changes: 40 additions & 12 deletions packages/neos-ui-sagas/src/Publish/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import {actionTypes, actions, selectors} from '@neos-project/neos-ui-redux-store
import {GlobalState} from '@neos-project/neos-ui-redux-store/src/System';
import {FeedbackEnvelope} from '@neos-project/neos-ui-redux-store/src/ServerFeedback';
import {PublishingMode, PublishingScope} from '@neos-project/neos-ui-redux-store/src/CR/Publishing';
import {Conflict} from '@neos-project/neos-ui-redux-store/src/CR/Syncing';
import backend, {Routes} from '@neos-project/neos-ui-backend-connector';

import {makeReloadNodes} from '../CR/NodeOperations/reloadNodes';
import {updateWorkspaceInfo} from '../CR/Workspaces';
import {makeResolveConflicts, makeSyncPersonalWorkspace} from '../Sync';

const handleWindowBeforeUnload = (event: BeforeUnloadEvent) => {
event.preventDefault();
Expand All @@ -32,6 +34,7 @@ type PublishingResponse =
numberOfAffectedChanges: number;
}
}
| { conflicts: Conflict[] }
| { error: AnyError };

export function * watchPublishing({routes}: {routes: Routes}) {
Expand Down Expand Up @@ -67,6 +70,8 @@ export function * watchPublishing({routes}: {routes: Routes}) {
};

const reloadAfterPublishing = makeReloadAfterPublishing({routes});
const syncPersonalWorkspace = makeSyncPersonalWorkspace({routes});
const resolveConflicts = makeResolveConflicts({syncPersonalWorkspace});

yield takeEvery(actionTypes.CR.Publishing.STARTED, function * publishingWorkflow(action: ReturnType<typeof actions.CR.Publishing.start>) {
const confirmed = yield * waitForConfirmation();
Expand All @@ -88,21 +93,37 @@ export function * watchPublishing({routes}: {routes: Routes}) {
? yield select(ancestorIdSelector)
: null;

function * attemptToPublishOrDiscard(): Generator<any, any, any> {
const result: PublishingResponse = scope === PublishingScope.ALL
? yield call(endpoint as any, workspaceName)
: yield call(endpoint!, ancestorId, workspaceName, dimensionSpacePoint);

if ('success' in result) {
yield put(actions.CR.Publishing.succeed(result.success.numberOfAffectedChanges));
yield * reloadAfterPublishing();
} else if ('conflicts' in result) {
yield put(actions.CR.Publishing.conflicts());
const conflictsWereResolved: boolean =
yield * resolveConflicts(result.conflicts);

if (conflictsWereResolved) {
yield put(actions.CR.Publishing.resolveConflicts());
yield * attemptToPublishOrDiscard();
} else {
yield put(actions.CR.Publishing.cancel());
yield call(updateWorkspaceInfo);
}
} else if ('error' in result) {
yield put(actions.CR.Publishing.fail(result.error));
} else {
yield put(actions.CR.Publishing.fail(null));
}
}

do {
try {
window.addEventListener('beforeunload', handleWindowBeforeUnload);
const result: PublishingResponse = scope === PublishingScope.ALL
? yield call(endpoint as any, workspaceName)
: yield call(endpoint, ancestorId, workspaceName, dimensionSpacePoint);

if ('success' in result) {
yield put(actions.CR.Publishing.succeed(result.success.numberOfAffectedChanges));
yield * reloadAfterPublishing();
} else if ('error' in result) {
yield put(actions.CR.Publishing.fail(result.error));
} else {
yield put(actions.CR.Publishing.fail(null));
}
yield * attemptToPublishOrDiscard();
} catch (error) {
yield put(actions.CR.Publishing.fail(error as AnyError));
} finally {
Expand All @@ -127,6 +148,13 @@ function * waitForConfirmation() {
}

function * waitForRetry() {
const isOngoing: boolean = yield select(
(state: GlobalState) => state.cr.publishing !== null
);
if (!isOngoing) {
return false;
}

const {retried}: {
acknowledged: ReturnType<typeof actions.CR.Publishing.acknowledge>;
retried: ReturnType<typeof actions.CR.Publishing.retry>;
Expand Down
39 changes: 27 additions & 12 deletions packages/neos-ui-sagas/src/Sync/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function * waitForConfirmation() {
return Boolean(confirmed);
}

const makeSyncPersonalWorkspace = (deps: {
export const makeSyncPersonalWorkspace = (deps: {
routes: Routes
}) => {
const refreshAfterSyncing = makeRefreshAfterSyncing(deps);
Expand Down Expand Up @@ -94,26 +94,41 @@ const makeSyncPersonalWorkspace = (deps: {
return syncPersonalWorkspace;
}

const makeResolveConflicts = (deps: {
export const makeResolveConflicts = (deps: {
syncPersonalWorkspace: ReturnType<typeof makeSyncPersonalWorkspace>
}) => {
const discardAll = makeDiscardAll(deps);

function * resolveConflicts(conflicts: Conflict[]): any {
yield put(actions.CR.Syncing.resolve(conflicts));

yield takeEvery<ReturnType<typeof actions.CR.Syncing.selectResolutionStrategy>>(
actionTypes.CR.Syncing.RESOLUTION_STARTED,
function * resolve({payload: {strategy}}) {
if (strategy === ResolutionStrategy.FORCE) {
if (yield * waitForResolutionConfirmation()) {
yield * deps.syncPersonalWorkspace(true);
}
} else if (strategy === ResolutionStrategy.DISCARD_ALL) {
yield * discardAll();
const {started}: {
cancelled: null | ReturnType<typeof actions.CR.Syncing.cancel>;
started: null | ReturnType<typeof actions.CR.Syncing.selectResolutionStrategy>;
} = yield race({
cancelled: take(actionTypes.CR.Syncing.CANCELLED),
started: take(actionTypes.CR.Syncing.RESOLUTION_STARTED)
});

if (started) {
const {payload: {strategy}} = started;

if (strategy === ResolutionStrategy.FORCE) {
if (yield * waitForResolutionConfirmation()) {
yield * deps.syncPersonalWorkspace(true);
return true;
}

return false;
}
);

if (strategy === ResolutionStrategy.DISCARD_ALL) {
yield * discardAll();
return true;
}
}

return false;
}

return resolveConflicts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ const PublishingDialog: React.FC<PublishingDialogProps> = (props) => {
/>
);

case PublishingPhase.CONFLICTS:
return null;

case PublishingPhase.ERROR:
case PublishingPhase.SUCCESS:
return (
Expand Down

0 comments on commit 926004b

Please sign in to comment.