Skip to content

Commit 7d7e257

Browse files
committed
fix: ref.inactive issues, with transient merging specifically
Adds ValaaReference.getObjectId Adds createMaterializeGhostReferenceAction Adds ghost.js validation for active partition case
1 parent 17c35b6 commit 7d7e257

File tree

6 files changed

+58
-37
lines changed

6 files changed

+58
-37
lines changed

packages/engine/Vrapper/Vrapper.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,12 @@ export default class Vrapper extends Cog {
293293
const transient = refreshingTransient
294294
|| resolver.goToTransient(this[HostRef], this._typeName);
295295
this.updateTransient(resolver.state, transient);
296-
this[HostRef] = transient.get("id");
296+
const id = transient.get("id");
297+
if (!id.getPartitionURI() && !id.isGhost() && (this._typeName !== "Blob")) {
298+
throw new Error(`Cannot update a non-ghost Vrapper with new id without partitionURI: <${
299+
id}>, (current id: <${this[HostRef]}>`);
300+
}
301+
this[HostRef] = id;
297302
const connection = this.tryPartitionConnection();
298303
if (!connection || !connection.isActive()) {
299304
if (this[HostRef].isInactive()) return this;

packages/raem/ValaaReference.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ export default @vdocorate([
6262
* @class VRef
6363
*/
6464
class ValaaReference {
65-
_isInactive: ?boolean;
66-
_mostInheritedMaterializedTransient: Object;
67-
6865
_nss: string; // urn namespace-specific string ie. valos rawId
6966
// urn components
7067
_q: Object; // urn q-component ie. query
@@ -104,6 +101,10 @@ class ValaaReference {
104101
return this;
105102
}
106103

104+
getObjectId (): VRef {
105+
if (this.hasOwnProperty("_nss")) return this;
106+
return this.getObjectId.call(Object.getPrototypeOf(this));
107+
}
107108
getQueryComponent (): Object { return this._q; }
108109
getResolverComponent (): Object { return this._r; }
109110
getFragmentComponent (): string { return this._f; }

packages/raem/redux/reducers/construct.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { duplicateFields } from "~/raem/redux/reducers/duplicate";
1515
import isResourceType from "~/raem/tools/graphql/isResourceType";
1616
import fieldInitialValue from "~/raem/tools/graphql/fieldInitialValue";
1717
import { addCoupleCouplingPassages } from "~/raem/tools/denormalized/couplings";
18-
import { createMaterializeGhostPathAction } from "~/raem/tools/denormalized/ghost";
18+
import { createMaterializeGhostReferenceAction } from "~/raem/tools/denormalized/ghost";
1919
import { setCreatedObjectPartition, universalizePartitionMutation }
2020
from "~/raem/tools/denormalized/partitions";
2121

@@ -70,7 +70,7 @@ export function prepareCreateOrDuplicateObjectTransientAndId (bard: CreateBard,
7070
invariantifyString(typeName, "CREATED.typeName required");
7171
bard.updateState(
7272
bard.subReduce(bard.state,
73-
createMaterializeGhostPathAction(bard, passage.id.getGhostPath(), typeName)));
73+
createMaterializeGhostReferenceAction(bard, passage.id, typeName)));
7474
bard.goToTransientOfRawId(passage.id.rawId());
7575
passage.id = bard.objectTransient.get("id");
7676
if (!passage.id) throw new Error("INTERNAL ERROR: no bard.objectTransient.get('id')");

packages/raem/redux/reducers/modify.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default function modifyResource (bard: Bard) {
5252
bard.goToTransientOfPassageObject(); // no-require, non-ghost-lookup
5353
if (!bard.objectTransient) { // ghost, inactive transient or fail
5454
const materializeGhostSubCommand = createMaterializeTransientAction(
55-
bard, passage.id.getGhostPath(), passage.typeName);
55+
bard, passage.id, passage.typeName);
5656
bard.updateState(bard.subReduce(bard.state, materializeGhostSubCommand));
5757
bard.goToTransientOfRawId(passage.id.rawId());
5858
passage.id = bard.objectId;

packages/raem/tools/denormalized/couplings.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ export function addCouplingPassages (bard: Bard, fieldIntro, remote: IdData, cou
101101
dumpify(remote, { sliceAt: 100 }), "coupling", dumpify(coupling, { sliceAt: 100 }));
102102
*/
103103
if (!remote /* || remote.isInactive() */) return;
104-
const remoteVRef = bard.obtainReference(remote);
105-
let coupledField = remoteVRef.getCoupledField();
104+
const remoteRef = bard.obtainReference(remote);
105+
let coupledField = remoteRef.getCoupledField();
106106
let remoteTypeName = remoteType.name;
107107
let remoteFieldIntro = remoteType.getFields()[coupledField];
108108
let reverseCoupling;
@@ -121,7 +121,7 @@ export function addCouplingPassages (bard: Bard, fieldIntro, remote: IdData, cou
121121
return;
122122
}
123123
if (!remoteFieldIntro) {
124-
remoteTransient = Object.create(bard).goToTransient(remoteVRef, remoteType.name);
124+
remoteTransient = Object.create(bard).goToTransient(remoteRef, remoteType.name);
125125
remoteTypeName = getTransientTypeName(remoteTransient, bard.schema);
126126
remoteFieldIntro = bard.schema.getType(remoteTypeName).getFields()[coupledField];
127127
if (!remoteFieldIntro) {
@@ -138,24 +138,25 @@ export function addCouplingPassages (bard: Bard, fieldIntro, remote: IdData, cou
138138
if (actionType === COUPLE_COUPLING) {
139139
bard.addPassage(
140140
reverseCoupling.createCoupleToRemoteAction(
141-
remoteVRef, remoteTypeName, coupledField, bard.objectId, fieldIntro.name));
141+
remoteRef.getObjectId(), remoteTypeName, coupledField, bard.objectId,
142+
fieldIntro.name));
142143
} else {
143144
if (coupling.preventsDestroy && (actionType === DESTROY_COUPLING)) {
144145
// Check if remote is in other partition as they can't prevent destroy, otherwise throw.
145-
const partitionURI = remoteVRef.getPartitionURI();
146+
const partitionURI = remoteRef.getPartitionURI();
146147
// Missing partitionURI means local partition reference, so throw.
147148
if (!partitionURI || (partitionURI.toString() === bard.destroyedResourcePartition)) {
148149
const nameBard = Object.create(bard);
149150
const name = bard.objectTypeIntro.getFields().name
150151
? `'${getObjectField(nameBard, bard.objectTransient, "name")}' `
151152
: bard.objectId.rawId();
152-
remoteTransient = Object.create(bard).goToTransient(remoteVRef, remoteType.name);
153+
remoteTransient = Object.create(bard).goToTransient(remoteRef, remoteType.name);
153154
const remoteName = bard.schema
154155
.getType(getTransientTypeName(remoteTransient, bard.schema))
155156
.getFields().name
156157
? `'${getObjectField(nameBard, remoteTransient, "name")}' `
157-
: remoteVRef.rawId();
158-
const remoteChapter = bard.obtainResourceChapter(remoteVRef.rawId());
158+
: remoteRef.rawId();
159+
const remoteChapter = bard.obtainResourceChapter(remoteRef.rawId());
159160
(remoteChapter.preventsDestroys || (remoteChapter.preventsDestroys = [])).push({
160161
// Flips the perspective: from the perspective of remote side, this side is the remote.
161162
name: remoteName,
@@ -168,7 +169,8 @@ export function addCouplingPassages (bard: Bard, fieldIntro, remote: IdData, cou
168169
}
169170
bard.addPassage(
170171
reverseCoupling.createUncoupleFromRemoteAction(
171-
remoteVRef, remoteTypeName, coupledField, bard.objectId, fieldIntro.name));
172+
remoteRef.getObjectId(), remoteTypeName, coupledField, bard.objectId,
173+
fieldIntro.name));
172174
}
173175
} catch (error) {
174176
throw bard.wrapErrorEvent(error,

packages/raem/tools/denormalized/ghost.js

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export function createGhostVRefInInstance (prototypeId: VRef,
8181

8282
export function createMaterializeGhostEvent (resolver: Resolver, ghostId: VRef): ?Action {
8383
try {
84-
return createMaterializeGhostPathAction(resolver, ghostId.getGhostPath(), undefined, true);
84+
return createMaterializeGhostReferenceAction(resolver, ghostId, undefined, true);
8585
} catch (error) {
8686
throw wrapError(error, `During createMaterializeGhostEvent(), with:`,
8787
"\n\tghostId:", ...dumpObject(ghostId));
@@ -100,15 +100,22 @@ export function createMaterializeGhostPathAction (resolver: Resolver, ghostObjec
100100
typeName: string, isEvent: ?boolean): ?Action {
101101
const actions = [];
102102
invariantify(ghostObjectPath.isGhost(), "materializeGhostPathAction.ghostObjectPath.isGhost");
103-
_createMaterializeGhostAction(resolver, resolver.getState(), ghostObjectPath, typeName, isEvent,
104-
actions);
105-
return !actions.length ? undefined
106-
: actions.length === 1 ? actions[0]
107-
: transacted({ actions });
103+
_createMaterializeGhostAction(resolver, resolver.getState(), ghostObjectPath, typeName,
104+
isEvent, undefined, actions);
105+
return !actions.length ? undefined : actions.length === 1 ? actions[0] : transacted({ actions });
106+
}
107+
108+
export function createMaterializeGhostReferenceAction (resolver: Resolver, ghostId: VRef,
109+
typeName: string, isEvent: ?boolean): ?Action {
110+
const actions = [];
111+
invariantify(ghostId.isGhost(), "materializeGhostPathAction.ghostObjectPath.isGhost");
112+
_createMaterializeGhostAction(resolver, resolver.getState(), ghostId.getGhostPath(), typeName,
113+
isEvent, ghostId, actions);
114+
return !actions.length ? undefined : actions.length === 1 ? actions[0] : transacted({ actions });
108115
}
109116

110117
/**
111-
* Like createMaterializeGhostPathAction but allows creationg of
118+
* Like createMaterializeGhostEvent but allows creationg of
112119
* a transient for non-ghost resources as well.
113120
*
114121
* @export
@@ -117,19 +124,18 @@ export function createMaterializeGhostPathAction (resolver: Resolver, ghostObjec
117124
* @param {string} typeName
118125
* @returns {?Action}
119126
*/
120-
export function createMaterializeTransientAction (resolver: Resolver, ghostObjectPath: GhostPath,
121-
typeName: string): ?Action {
127+
export function createMaterializeTransientAction (resolver: Resolver, id: VRef, typeName: string):
128+
?Action {
122129
const actions = [];
123-
_createMaterializeGhostAction(resolver, resolver.getState(), ghostObjectPath, typeName, false,
124-
actions);
125-
return !actions.length ? undefined
126-
: actions.length === 1 ? actions[0]
127-
: transacted({ actions });
130+
_createMaterializeGhostAction(resolver, resolver.getState(), id.getGhostPath(), typeName,
131+
false, id, actions);
132+
return !actions.length ? undefined : actions.length === 1 ? actions[0] : transacted({ actions });
128133
}
129134

130135
function _createMaterializeGhostAction (resolver: Resolver, state: State,
131-
ghostObjectPath: GhostPath, typeName: string, isEvent: boolean,
132-
outputActions: Array<Action>): { id: string, actualType: string, ghostPath: GhostPath } {
136+
ghostObjectPath: GhostPath, typeName: string, isEvent: boolean, knownId: ?VRef,
137+
outputActions: Array<Action>,
138+
): { id: string, actualType: string, ghostPath: GhostPath } {
133139
// TODO(iridian): This whole segment needs to be re-evaluated now
134140
// with the introduction of the "ghostOwnlings"/"ghostOwner" coupling
135141
// introduction. Specifically: owners would not need to be
@@ -161,28 +167,34 @@ function _createMaterializeGhostAction (resolver: Resolver, state: State,
161167
// the referred resource doesn't exist. As it stands there is no
162168
// theoretical way to determine the actual partition id reliably,
163169
// either. Create an inactive reference for the resource.
164-
const id = vRef(ghostRawId);
165-
// TODO(iridian): Add inactive partition checks: throw if this partition is in fact active.
170+
const id = knownId || vRef(ghostRawId);
171+
if (knownId && !knownId.isInactive()) {
172+
throw new Error("Cannot materialize a non-existent resource (partition is active)");
173+
}
174+
// Make also the resource inactive, not the partition reference
175+
// prototype. This way only transient merging will activate it.
166176
id.setInactive();
167177
actualType = resolver.schema.inactiveType.name;
168178
outputActions.push(created({
169179
id, typeName: actualType,
170180
meta: { noSubMaterialize: !isEvent },
171181
}));
172-
return { id, actualType, ghostPath: id.getGhostPath };
182+
return { id, actualType, ghostPath: id.getGhostPath() };
173183
}
174184
// A regular non-root ghost Resource with no transient.
175185
// Still possibly inside an inactive partition.
176186
const { id: ghostPrototype, actualType: prototypeTypeName, ghostPath: prototypePath }
177187
= _createMaterializeGhostAction(resolver, state, ghostObjectPath.previousStep(), typeName,
178-
false, outputActions);
188+
false, undefined, outputActions);
179189
actualType = isInactiveTypeName(prototypeTypeName) ? typeName : prototypeTypeName;
180190
const ghostPath = prototypePath
181191
.withNewStep(ghostHostPrototypeRawId, ghostHostRawId, ghostRawId);
182192
const hostType = state.getIn(["TransientFields", ghostHostRawId]);
183193
const hostId = hostType
184194
? state.getIn([hostType, ghostHostRawId]).get("id")
185-
: Object.create(new ValaaReference().initResolverComponent({ inactive: true }))
195+
: Object.create(knownId
196+
? Object.getPrototypeOf(knownId)
197+
: new ValaaReference().initResolverComponent({ inactive: true }))
186198
.initNSS(ghostHostRawId);
187199
const id = Object.create(Object.getPrototypeOf(hostId)).initNSS(ghostRawId);
188200
id.connectGhostPath(ghostPath);
@@ -196,6 +208,7 @@ function _createMaterializeGhostAction (resolver: Resolver, state: State,
196208
throw wrapError(error, `During createMaterializeGhostAction(${dumpify(ghostObjectPath)}:${
197209
typeName}/${actualType}}), with:`,
198210
"\n\ttransientType:", actualType,
211+
"\n\tknownId:", knownId,
199212
"\n\tghost host prototype:", ghostHostPrototypeRawId,
200213
"\n\tghost host:", ghostHostRawId,
201214
"\n\tghost id:", ghostRawId);

0 commit comments

Comments
 (0)