Skip to content

Commit

Permalink
Remove proxy for interface
Browse files Browse the repository at this point in the history
  • Loading branch information
ericanderson committed Dec 5, 2024
1 parent d12858c commit bd7b149
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 190 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,9 @@ import type {
import type { ObjectHolder } from "./ObjectHolder.js";

/** @internal */
export interface InterfaceHolderOwnProps<
export interface InterfaceHolder<
Q extends FetchedObjectTypeDefinition,
> {
[UnderlyingOsdkObject]: Osdk<Q> & ObjectHolder<Q>;
[InterfaceDefRef]: InterfaceMetadata;
}

/** @internal */
export interface InterfaceHolder<
Q extends FetchedObjectTypeDefinition,
> extends InterfaceHolderOwnProps<Q> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@ export const InterfaceDefRef = Symbol(
process.env.MODE !== "production" ? "InterfaceDefinition" : undefined,
);

/** A symbol for getting the raw data object off of the holder an osdk object proxy points to */
/** @internal */
export const RawObject = Symbol(
process.env.MODE !== "production" ? "RawObject" : undefined,
);

/** @internal */
export const ClientRef = Symbol(
process.env.MODE !== "production" ? "ClientRef" : undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,21 @@
*/

import type { Osdk } from "@osdk/api";
import type { OntologyObjectV2 } from "@osdk/internal.foundry.core";
import type { MinimalClient } from "../../MinimalClientContext.js";
import type { FetchedObjectTypeDefinition } from "../../ontology/OntologyProvider.js";
import type { DollarAsFn } from "./getDollarAs.js";
import type { get$link } from "./getDollarLink.js";
import type {
ClientRef,
ObjectDefRef,
RawObject,
UnderlyingOsdkObject,
} from "./InternalSymbols.js";

/** @internal */
export interface ObjectHolderPrototypeOwnProps {
export interface ObjectHolder<Q extends FetchedObjectTypeDefinition> {
readonly [UnderlyingOsdkObject]: Osdk<Q>;
readonly [ObjectDefRef]: FetchedObjectTypeDefinition;
readonly [ClientRef]: MinimalClient;
readonly "$as": DollarAsFn;
readonly "$link": ReturnType<typeof get$link>;
}
/** @internal */
export interface ObjectHolderOwnProperties {
[RawObject]: OntologyObjectV2;
}

/** @internal */
export interface ObjectHolder<Q extends FetchedObjectTypeDefinition>
extends ObjectHolderPrototypeOwnProps, ObjectHolderOwnProperties
{
[UnderlyingOsdkObject]: Osdk<Q>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ describe(createOsdkInterface, () => {
status: "ACTIVE",
} satisfies FetchedObjectTypeDefinition,
};
// underlying: Osdk<Q> & ObjectHolder<Q>,
// interfaceDef: InterfaceMetadata,

const iface = createOsdkInterface(underlying as any, {
"apiName": "IFoo",
displayName: "",
Expand Down Expand Up @@ -114,8 +113,7 @@ describe(createOsdkInterface, () => {
status: "ACTIVE",
} satisfies FetchedObjectTypeDefinition,
};
// underlying: Osdk<Q> & ObjectHolder<Q>,
// interfaceDef: InterfaceMetadata,

const iface = createOsdkInterface(underlying as any, {
"apiName": "a.IFoo",
displayName: "",
Expand Down Expand Up @@ -176,8 +174,7 @@ describe(createOsdkInterface, () => {
status: "ACTIVE",
} satisfies FetchedObjectTypeDefinition,
};
// underlying: Osdk<Q> & ObjectHolder<Q>,
// interfaceDef: InterfaceMetadata,

const iface = createOsdkInterface(underlying as any, {
"apiName": "a.IFoo",
displayName: "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,157 +14,70 @@
* limitations under the License.
*/

import type { InterfaceMetadata, Osdk, OsdkBase } from "@osdk/api";
import type { InterfaceMetadata, Osdk } from "@osdk/api";
import { extractNamespace } from "../../internal/conversions/modernToLegacyWhereClause.js";
import type { FetchedObjectTypeDefinition } from "../../ontology/OntologyProvider.js";
import { createSimpleCache } from "../SimpleCache.js";
import type {
InterfaceHolder,
InterfaceHolderOwnProps,
} from "./InterfaceHolder.js";
import type { InterfaceHolder } from "./InterfaceHolder.js";
import {
InterfaceDefRef,
ObjectDefRef,
UnderlyingOsdkObject,
} from "./InternalSymbols.js";
import type { ObjectHolder } from "./ObjectHolder.js";

const handlerCache = createSimpleCache<
InterfaceMetadata,
ProxyHandler<InterfaceHolder<any> & Osdk<any>>
>(
new WeakMap(),
createInterfaceProxyHandler,
);

/** @internal */
export function createOsdkInterface<
Q extends FetchedObjectTypeDefinition,
>(
underlying: Osdk<Q> & ObjectHolder<Q>,
interfaceDef: InterfaceMetadata,
) {
const interfaceHolder: InterfaceHolderOwnProps<Q> = Object.create(null, {
[UnderlyingOsdkObject]: { value: underlying },
[InterfaceDefRef]: { value: interfaceDef },
});

const handler = handlerCache.get(interfaceDef);

const proxy = new Proxy<OsdkBase<any>>(
interfaceHolder as unknown as OsdkBase<any>, // the wrapper doesn't contain everything obviously. we proxy
handler,
const [objApiNamespace] = extractNamespace(interfaceDef.apiName);

return Object.freeze(
Object.defineProperties({}, {
// first to minimize hidden classes
[UnderlyingOsdkObject]: { value: underlying },

"$apiName": { value: interfaceDef.apiName, enumerable: true },
"$as": {
value: underlying.$as,
enumerable: false,
},
"$objectType": {
value: underlying.$objectType,
enumerable: "$objectType" in underlying,
},
"$primaryKey": {
value: underlying.$primaryKey,
enumerable: "$primaryKey" in underlying,
},
"$title": {
value: underlying.$title,
enumerable: "$title" in underlying,
},
"$rid": {
value: (underlying as any).$rid,
enumerable: "$rid" in underlying,
},

[InterfaceDefRef]: { value: interfaceDef },

...Object.fromEntries(
Object.keys(interfaceDef.properties).map(p => {
const objDef = underlying[ObjectDefRef];

const [apiNamespace, apiName] = extractNamespace(p);

const targetPropName = objDef
.interfaceMap![interfaceDef.apiName][p];

return [apiNamespace === objApiNamespace ? apiName : p, {
enumerable: targetPropName in underlying,
value: underlying[targetPropName as keyof typeof underlying],
}];
}),
),
}) as InterfaceHolder<any> & Osdk<any>,
);
return proxy;
}

function createInterfaceProxyHandler(
newDef: InterfaceMetadata,
): ProxyHandler<InterfaceHolder<any> & Osdk<any>> {
return {
getOwnPropertyDescriptor(target, p) {
const underlying = target[UnderlyingOsdkObject];
const objDef = underlying[ObjectDefRef];

switch (p) {
case UnderlyingOsdkObject:
case InterfaceDefRef:
return Reflect.getOwnPropertyDescriptor(target, p);

case "$primaryKey":
case "$title":
case "$objectType":
case "$rid":
return p in underlying
? {
value: underlying[p],
configurable: true,
enumerable: true,
}
: undefined;

case "$apiName":
return {
enumerable: true,
configurable: true,
value: target[InterfaceDefRef].apiName,
};
}

const [objApiNamespace] = extractNamespace(newDef.apiName);
if (objApiNamespace != null) {
const [apiNamespace, apiName] = extractNamespace(p as string);
if (apiNamespace == null) {
p = `${objApiNamespace}.${apiName}`;
}
}

if (newDef.properties[p as string] != null) {
return {
enumerable: true,
configurable: true,
value: underlying[
objDef.interfaceMap![newDef.apiName][p as string] as any
],
};
}
},

ownKeys(target) {
const underlying = target[UnderlyingOsdkObject];
const [objApiNamespace] = extractNamespace(newDef.apiName);
let propNames = Object.keys(newDef.properties);

if (objApiNamespace != null) {
propNames = propNames.map(p => {
const [apiNamespace, apiName] = extractNamespace(p as string);
if (apiNamespace === objApiNamespace) {
p = apiName;
}
return p;
});
}

return [
"$apiName",
"$objectType",
"$primaryKey",
...(underlying["$rid"] ? ["$rid"] : []),
"$title",
UnderlyingOsdkObject,
InterfaceDefRef,
...propNames,
];
},

get(target, p) {
const underlying = target[UnderlyingOsdkObject];
switch (p) {
case InterfaceDefRef:
return newDef;
case "$apiName":
return newDef.apiName;
case "$as":
case UnderlyingOsdkObject:
case "$primaryKey":
case "$title":
case "$objectType":
case "$rid":
return underlying[p as string];
}

const [objApiNamespace] = extractNamespace(newDef.apiName);
if (objApiNamespace != null) {
const [apiNamespace, apiName] = extractNamespace(p as string);
if (apiNamespace == null) {
p = `${objApiNamespace}.${apiName}`;
}
}

if (newDef.properties[p as string] != null) {
const objDef = target[UnderlyingOsdkObject][ObjectDefRef];
return underlying[objDef.interfaceMap![newDef.apiName][p as string]];
}
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,11 @@ import { get$link } from "./getDollarLink.js";
import {
ClientRef,
ObjectDefRef,
RawObject,
UnderlyingOsdkObject,
} from "./InternalSymbols.js";
import type { ObjectHolder } from "./ObjectHolder.js";

interface InternalOsdkInstance {
[RawObject]: OntologyObjectV2;
[ObjectDefRef]: FetchedObjectTypeDefinition;
[ClientRef]: MinimalClient;
}
Expand Down Expand Up @@ -62,7 +60,6 @@ const basePropDefs = {
[UnderlyingOsdkObject]: this as any,
[ObjectDefRef]: this[ObjectDefRef],
[ClientRef]: this[ClientRef],
[RawObject]: this[RawObject],
} as ObjectHolder<any>);
},
},
Expand All @@ -78,18 +75,13 @@ export function createOsdkObject<
): Osdk<ObjectTypeDefinition, any> {
// updates the object's "hidden class/map".
Object.defineProperties(rawObj, {
[ObjectDefRef]: { value: objectDef, enumerable: false },
[ClientRef]: { value: client, enumerable: false },
[RawObject]: {
value: rawObj,
enumerable: false,
},
...basePropDefs,

[UnderlyingOsdkObject]: {
enumerable: false,
value: rawObj,
},
[ObjectDefRef]: { value: objectDef, enumerable: false },
[ClientRef]: { value: client, enumerable: false },
...basePropDefs,
});

// Assign the special values
Expand All @@ -98,9 +90,6 @@ export function createOsdkObject<
propKey in objectDef.properties
&& specialPropertyTypes.has(objectDef.properties[propKey].type)
) {
const rawValue = rawObj[propKey as any];
const propDef = objectDef.properties[propKey as any];

rawObj[propKey] = createSpecialProperty(
client,
objectDef,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import type {
} from "@osdk/api";
import { getWireObjectSet } from "../../objectSet/createObjectSet.js";
import { fetchSingle, fetchSingleWithErrors } from "../fetchSingle.js";
import { ClientRef, ObjectDefRef, RawObject } from "./InternalSymbols.js";
import type {
ObjectHolder,
ObjectHolderOwnProperties,
} from "./ObjectHolder.js";
import {
ClientRef,
ObjectDefRef,
UnderlyingOsdkObject,
} from "./InternalSymbols.js";
import type { ObjectHolder } from "./ObjectHolder.js";

/** @internal */
export function get$link(
Expand All @@ -43,7 +44,7 @@ const DollarLinkProxyHandler: ProxyHandler<ObjectHolder<any>> = {
const {
[ObjectDefRef]: objDef,
[ClientRef]: client,
[RawObject]: rawObj,
[UnderlyingOsdkObject]: rawObj,
} = target;
const linkDef = objDef.links[p as string];
if (linkDef == null) {
Expand Down Expand Up @@ -80,7 +81,7 @@ const DollarLinkProxyHandler: ProxyHandler<ObjectHolder<any>> = {

ownKeys(
target: ObjectHolder<any>,
): ArrayLike<keyof ObjectHolderOwnProperties | string> {
): ArrayLike<keyof ObjectHolder<any> | string> {
return [...Object.keys(target[ObjectDefRef].links)];
},

Expand Down

0 comments on commit bd7b149

Please sign in to comment.