fix: bind Tomorrow conflict recovery to operator account
This commit is contained in:
parent
16a6ee1136
commit
059722de6c
|
|
@ -63,14 +63,15 @@ function createTomorrowPlan({fetchJson,localDate,timeZone,storage,getLogin}={})
|
||||||
}).catch(async error=>{
|
}).catch(async error=>{
|
||||||
if(error?.status===409) {
|
if(error?.status===409) {
|
||||||
const remote=await fetchJson('api/v1/tomorrow');
|
const remote=await fetchJson('api/v1/tomorrow');
|
||||||
lastConflict={local:pending(),remote:{...remote,ids:[...remote.ids],estimates:{...(remote.estimates||{})}}};
|
lastConflict={key,local:pending(),remote:{...remote,ids:[...remote.ids],estimates:{...(remote.estimates||{})}}};
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}).finally(()=>{flushing=null;});
|
}).finally(()=>{flushing=null;});
|
||||||
return flushing;
|
return flushing;
|
||||||
}
|
}
|
||||||
async function keepLocal() {
|
async function keepLocal() {
|
||||||
if(!lastConflict) return false;
|
if(!lastConflict||lastConflict.key!==storageKey()) return false;
|
||||||
|
const conflictKey=lastConflict.key;
|
||||||
const local={...lastConflict.local,ids:[...lastConflict.local.ids],estimates:{...lastConflict.local.estimates}};
|
const local={...lastConflict.local,ids:[...lastConflict.local.ids],estimates:{...lastConflict.local.estimates}};
|
||||||
const body=deliveryBody({...local,base_revision:lastConflict.remote.revision});
|
const body=deliveryBody({...local,base_revision:lastConflict.remote.revision});
|
||||||
try {
|
try {
|
||||||
|
|
@ -84,13 +85,13 @@ function createTomorrowPlan({fetchJson,localDate,timeZone,storage,getLogin}={})
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
if(error?.status===409) {
|
if(error?.status===409) {
|
||||||
const remote=await fetchJson('api/v1/tomorrow');
|
const remote=await fetchJson('api/v1/tomorrow');
|
||||||
lastConflict={local,remote:{...remote,ids:[...remote.ids],estimates:{...(remote.estimates||{})}}};
|
lastConflict={key:conflictKey,local,remote:{...remote,ids:[...remote.ids],estimates:{...(remote.estimates||{})}}};
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function useRemote() {
|
function useRemote() {
|
||||||
if(!lastConflict) return false;
|
if(!lastConflict||lastConflict.key!==storageKey()) return false;
|
||||||
const remote=lastConflict.remote;
|
const remote=lastConflict.remote;
|
||||||
const key=storageKey();
|
const key=storageKey();
|
||||||
if(key&&storage) storage.removeItem(key);
|
if(key&&storage) storage.removeItem(key);
|
||||||
|
|
@ -162,8 +163,8 @@ function createTomorrowPlan({fetchJson,localDate,timeZone,storage,getLogin}={})
|
||||||
return {run,stop(){if(timer!==null)clearTimer(timer);timer=null;}};
|
return {run,stop(){if(timer!==null)clearTimer(timer);timer=null;}};
|
||||||
}
|
}
|
||||||
function conflict() {
|
function conflict() {
|
||||||
return lastConflict&&{local:{...lastConflict.local,ids:[...lastConflict.local.ids],estimates:{...lastConflict.local.estimates}},
|
return lastConflict?.key===storageKey()?{local:{...lastConflict.local,ids:[...lastConflict.local.ids],estimates:{...lastConflict.local.estimates}},
|
||||||
remote:{...lastConflict.remote,ids:[...lastConflict.remote.ids],estimates:{...lastConflict.remote.estimates}}};
|
remote:{...lastConflict.remote,ids:[...lastConflict.remote.ids],estimates:{...lastConflict.remote.estimates}}}:null;
|
||||||
}
|
}
|
||||||
return {adopt,load,save,stage,pending,flush,conflict,keepLocal,useRemote,promote,state,summary,nextLocalDate,startLifecycle};
|
return {adopt,load,save,stage,pending,flush,conflict,keepLocal,useRemote,promote,state,summary,nextLocalDate,startLifecycle};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,37 @@ console.log(JSON.stringify({adopted,requests,pending:planner.pending(),conflict:
|
||||||
assert result["state"] == result["adopted"]
|
assert result["state"] == result["adopted"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_tomorrow_conflict_cannot_be_read_or_resolved_after_account_switch():
|
||||||
|
result = run_controller("""
|
||||||
|
const values=new Map();
|
||||||
|
const storage={getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)};
|
||||||
|
let login='timmy';
|
||||||
|
const fetchJson=async (_url,options={})=>{
|
||||||
|
if(options.method==='PUT'){const error=new Error('changed elsewhere');error.status=409;throw error;}
|
||||||
|
return {revision:12,ids:['issue:private:timmy:'],capacity_minutes:60,estimates:{},plan_date:'2026-08-20',timezone:'UTC'};
|
||||||
|
};
|
||||||
|
const planner=createTomorrowPlan({storage,getLogin:()=>login,fetchJson,
|
||||||
|
localDate:()=> '2026-08-19',timeZone:()=> 'UTC'});
|
||||||
|
planner.adopt({revision:11,ids:[],capacity_minutes:null,estimates:{}});
|
||||||
|
planner.stage({ids:['issue:private:phone:'],capacity_minutes:90,estimates:{}});
|
||||||
|
try { await planner.flush(); } catch(_error) {}
|
||||||
|
const before=planner.conflict();
|
||||||
|
login='alexander';
|
||||||
|
const after=planner.conflict();
|
||||||
|
const keep=await planner.keepLocal();
|
||||||
|
const use=planner.useRemote();
|
||||||
|
login='timmy';
|
||||||
|
const restored=planner.conflict();
|
||||||
|
console.log(JSON.stringify({before,after,keep,use,restored}));
|
||||||
|
""")
|
||||||
|
|
||||||
|
assert result["before"]["remote"]["ids"] == ["issue:private:timmy:"]
|
||||||
|
assert result["after"] is None
|
||||||
|
assert result["keep"] is False
|
||||||
|
assert result["use"] is False
|
||||||
|
assert result["restored"]["local"]["ids"] == ["issue:private:phone:"]
|
||||||
|
|
||||||
|
|
||||||
def test_tomorrow_planner_loads_and_saves_independently_from_today():
|
def test_tomorrow_planner_loads_and_saves_independently_from_today():
|
||||||
result = run_controller("""
|
result = run_controller("""
|
||||||
const requests=[];
|
const requests=[];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user