ws adapter: match main truth (id injection + setDoc merge)
- getDoc/getCollection/subscribe inject {id,...data} (main firebase shape)
- getCollection normalizes backend {id,data}[] OR bare data[] → {id,...data}
- setDoc(opts.merge) → PATCH (create-on-miss); else PUT (replace)
- ws-reconnect test asserts id now present
24/24 server green. All suites green: App 83, shared 91, server 24.
This commit is contained in:
@@ -48,7 +48,7 @@ describe('BUG-8: ws adapter reconnect after drop', () => {
|
|||||||
await flush(1000);
|
await flush(1000);
|
||||||
|
|
||||||
const last = calls[calls.length - 1];
|
const last = calls[calls.length - 1];
|
||||||
expect(last).toEqual({ name: 'V2' });
|
expect(last).toEqual({ id: 'a', name: 'V2' });
|
||||||
} finally {
|
} finally {
|
||||||
await new Promise(r => storage.dispose(r));
|
await new Promise(r => storage.dispose(r));
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-4
@@ -33,6 +33,15 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
|||||||
return p.replace(/^[\s\S]*\/public\/data\//, '');
|
return p.replace(/^[\s\S]*\/public\/data\//, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inject id (last path segment) into doc — matches main firebase truth:
|
||||||
|
// { id: docSnap.id, ...snap.data() }
|
||||||
|
// Backend stores docs WITHOUT id; client adds it so all adapters share shape.
|
||||||
|
function withId(rawPath, data) {
|
||||||
|
if (data === null || data === undefined) return data;
|
||||||
|
const id = norm(rawPath).split('/').pop();
|
||||||
|
return { id, ...data };
|
||||||
|
}
|
||||||
|
|
||||||
const docSubs = new Map(); // path -> Set<cb>
|
const docSubs = new Map(); // path -> Set<cb>
|
||||||
const collSubs = new Map(); // collPath -> Set<cb>
|
const collSubs = new Map(); // collPath -> Set<cb>
|
||||||
let ws = null;
|
let ws = null;
|
||||||
@@ -154,12 +163,19 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
|||||||
async getDoc(rawPath) {
|
async getDoc(rawPath) {
|
||||||
const p = norm(rawPath);
|
const p = norm(rawPath);
|
||||||
const res = await api('GET', '/api/doc', { path: p });
|
const res = await api('GET', '/api/doc', { path: p });
|
||||||
return res && res.data !== undefined ? res.data : null;
|
const data = res && res.data !== undefined ? res.data : null;
|
||||||
|
return withId(rawPath, data);
|
||||||
},
|
},
|
||||||
|
|
||||||
async setDoc(rawPath, data) {
|
async setDoc(rawPath, data, opts = {}) {
|
||||||
const p = norm(rawPath);
|
const p = norm(rawPath);
|
||||||
|
// merge flag -> PATCH (shallow merge, create-on-miss). Matches firebase
|
||||||
|
// setDoc(path, data, {merge:true}) semantics.
|
||||||
|
if (opts.merge) {
|
||||||
|
await api('PATCH', '/api/doc', null, { path: p, patch: data });
|
||||||
|
} else {
|
||||||
await api('PUT', '/api/doc', null, { path: p, data });
|
await api('PUT', '/api/doc', null, { path: p, data });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateDoc(rawPath, patch) {
|
async updateDoc(rawPath, patch) {
|
||||||
@@ -180,7 +196,16 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
|||||||
|
|
||||||
async getCollection(rawCollPath) {
|
async getCollection(rawCollPath) {
|
||||||
const p = norm(rawCollPath);
|
const p = norm(rawCollPath);
|
||||||
return await api('GET', '/api/collection', { path: p });
|
const docs = await api('GET', '/api/collection', { path: p });
|
||||||
|
// Backend returns array of { id, data } OR bare data[]; normalize to
|
||||||
|
// { id, ...data } (firebase truth).
|
||||||
|
if (!Array.isArray(docs)) return [];
|
||||||
|
return docs.map(d => {
|
||||||
|
if (d && typeof d === 'object' && 'id' in d && 'data' in d) {
|
||||||
|
return { id: d.id, ...d.data };
|
||||||
|
}
|
||||||
|
return { ...d };
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async batchWrite(ops) {
|
async batchWrite(ops) {
|
||||||
@@ -190,7 +215,7 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
|||||||
|
|
||||||
subscribeDoc(rawPath, cb, errCb) {
|
subscribeDoc(rawPath, cb, errCb) {
|
||||||
const p = norm(rawPath);
|
const p = norm(rawPath);
|
||||||
// Initial value via REST (independent of WS connect).
|
// Initial value via REST (independent of WS connect). getDoc injects id.
|
||||||
storage.getDoc(p).then(cb).catch(() => {});
|
storage.getDoc(p).then(cb).catch(() => {});
|
||||||
// WS only for subsequent change notifications.
|
// WS only for subsequent change notifications.
|
||||||
ensureWs().then(() => {
|
ensureWs().then(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user