Skip to content

Commit e415c57

Browse files
authored
code ref should serialize to relative (#2154)
* serialize to relative url only when id does not exist set null only when card.id does not exist * only use maybeRelativeURL for codeRefField * fix bug * client code can set maybeRelativeURL to absolute * fix test * attempt to string thru opts for every field * only try to serialize with primitive. Otherwise, too much influence on nested serialization * useAbsoluteURL: true optional paramter -- clearer usage of SerializedOpts * add test
1 parent 6008560 commit e415c57

File tree

7 files changed

+142
-33
lines changed

7 files changed

+142
-33
lines changed

packages/base/card-api.gts

+32-27
Original file line numberDiff line numberDiff line change
@@ -696,13 +696,18 @@ class Contains<CardT extends FieldDefConstructor> implements Field<CardT, any> {
696696
serialize(
697697
value: InstanceType<CardT>,
698698
doc: JSONAPISingleResourceDocument,
699+
_visited: Set<string>,
700+
opts?: SerializeOpts,
699701
): JSONAPIResource {
700-
let serialized: JSONAPISingleResourceDocument['data'] & {
701-
meta: Record<string, any>;
702-
} = callSerializeHook(this.card, value, doc);
703702
if (primitive in this.card) {
703+
let serialized: JSONAPISingleResourceDocument['data'] & {
704+
meta: Record<string, any>;
705+
} = callSerializeHook(this.card, value, doc, undefined, opts);
704706
return { attributes: { [this.name]: serialized } };
705707
} else {
708+
let serialized: JSONAPISingleResourceDocument['data'] & {
709+
meta: Record<string, any>;
710+
} = callSerializeHook(this.card, value, doc);
706711
let resource: JSONAPIResource = {
707712
attributes: {
708713
[this.name]: serialized?.attributes,
@@ -2299,8 +2304,9 @@ async function getDeserializedValue<CardT extends BaseDefConstructor>({
22992304
export interface SerializeOpts {
23002305
includeComputeds?: boolean;
23012306
includeUnrenderedFields?: boolean;
2302-
maybeRelativeURL?: ((possibleURL: string) => string) | null; // setting this to null will force all URL's to be absolute
2307+
useAbsoluteURL?: boolean;
23032308
omitFields?: [typeof BaseDef];
2309+
maybeRelativeURL?: (possibleURL: string) => string;
23042310
}
23052311

23062312
function serializeCardResource(
@@ -2309,7 +2315,10 @@ function serializeCardResource(
23092315
opts?: SerializeOpts,
23102316
visited: Set<string> = new Set(),
23112317
): LooseCardResource {
2312-
let adoptsFrom = identifyCard(model.constructor, opts?.maybeRelativeURL);
2318+
let adoptsFrom = identifyCard(
2319+
model.constructor,
2320+
opts?.useAbsoluteURL ? undefined : opts?.maybeRelativeURL,
2321+
);
23132322
if (!adoptsFrom) {
23142323
throw new Error(`bug: could not identify card: ${model.constructor.name}`);
23152324
}
@@ -2346,28 +2355,22 @@ export function serializeCard(
23462355
let modelRelativeTo = model[relativeTo];
23472356
let data = serializeCardResource(model, doc, {
23482357
...opts,
2349-
// if opts.maybeRelativeURL is null that is our indication
2350-
// that the caller wants all the URL's to be absolute
2351-
...(opts?.maybeRelativeURL !== null
2352-
? {
2353-
maybeRelativeURL(possibleURL: string) {
2354-
let url = maybeURL(possibleURL, modelRelativeTo);
2355-
if (!url) {
2356-
throw new Error(
2357-
`could not determine url from '${maybeRelativeURL}' relative to ${modelRelativeTo}`,
2358-
);
2359-
}
2360-
if (!modelRelativeTo) {
2361-
return url.href;
2362-
}
2363-
const realmURLString = getCardMeta(model, 'realmURL');
2364-
const realmURL = realmURLString
2365-
? new URL(realmURLString)
2366-
: undefined;
2367-
return maybeRelativeURL(url, modelRelativeTo, realmURL);
2368-
},
2358+
...{
2359+
maybeRelativeURL(possibleURL: string) {
2360+
let url = maybeURL(possibleURL, modelRelativeTo);
2361+
if (!url) {
2362+
throw new Error(
2363+
`could not determine url from '${maybeRelativeURL}' relative to ${modelRelativeTo}`,
2364+
);
23692365
}
2370-
: {}),
2366+
if (!modelRelativeTo) {
2367+
return url.href;
2368+
}
2369+
const realmURLString = getCardMeta(model, 'realmURL');
2370+
const realmURL = realmURLString ? new URL(realmURLString) : undefined;
2371+
return maybeRelativeURL(url, modelRelativeTo, realmURL);
2372+
},
2373+
},
23712374
});
23722375
merge(doc, { data });
23732376
if (!isSingleCardDocument(doc)) {
@@ -3150,7 +3153,9 @@ export class Box<T> {
31503153
type ElementType<T> = T extends (infer V)[] ? V : never;
31513154

31523155
function makeRelativeURL(maybeURL: string, opts?: SerializeOpts): string {
3153-
return opts?.maybeRelativeURL ? opts.maybeRelativeURL(maybeURL) : maybeURL;
3156+
return opts?.maybeRelativeURL && !opts?.useAbsoluteURL
3157+
? opts.maybeRelativeURL(maybeURL)
3158+
: maybeURL;
31543159
}
31553160

31563161
declare module 'ember-provide-consume-context/context-registry' {

packages/base/code-ref.gts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default class CodeRefField extends FieldDef {
3939
) {
4040
return {
4141
...codeRef,
42-
...(opts?.maybeRelativeURL
42+
...(opts?.maybeRelativeURL && !opts?.useAbsoluteURL
4343
? { module: opts.maybeRelativeURL(codeRef.module) }
4444
: {}),
4545
};

packages/host/app/commands/add-skills-to-room.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export default class AddSkillsToRoomCommand extends HostBaseCommand<
2727
let roomSkillEventIds = await matrixService.addSkillCardsToRoomHistory(
2828
skills,
2929
roomId,
30-
{ includeComputeds: true, maybeRelativeURL: null },
30+
{ includeComputeds: true, useAbsoluteURL: true },
3131
);
3232
await matrixService.updateStateEvent(
3333
roomId,

packages/host/app/services/card-service.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ export default class CardService extends Service {
213213
// for a brand new card that has no id yet, we don't know what we are
214214
// relativeTo because its up to the realm server to assign us an ID, so
215215
// URL's should be absolute
216-
maybeRelativeURL: null, // forces URL's to be absolute.
216+
useAbsoluteURL: true,
217217
});
218+
218219
// send doc over the wire with absolute URL's. The realm server will convert
219220
// to relative URL's as it serializes the cards
220221
let realmURL = await this.getRealmURL(card);
@@ -423,7 +424,7 @@ export default class CardService extends Service {
423424
async copyCard(source: CardDef, destinationRealm: URL): Promise<CardDef> {
424425
let api = await this.getAPI();
425426
let serialized = await this.serializeCard(source, {
426-
maybeRelativeURL: null, // forces URL's to be absolute.
427+
useAbsoluteURL: true,
427428
});
428429
delete serialized.data.id;
429430
let json = await this.saveCardDocument(serialized, destinationRealm);

packages/host/app/services/matrix-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ export default class MatrixService extends Service {
590590
cards: CardDef[],
591591
roomId: string,
592592
cardHashes: Map<string, string> = this.cardHashes,
593-
opts: CardAPI.SerializeOpts = { maybeRelativeURL: null },
593+
opts: CardAPI.SerializeOpts = { useAbsoluteURL: true },
594594
): Promise<string[]> {
595595
if (!cards.length) {
596596
return [];

packages/host/tests/integration/components/card-catalog-test.gts

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ module('Integration | card-catalog', function (hooks) {
120120
description: 'Spec for PublishingPacket',
121121
specType: 'card',
122122
ref: {
123-
module: `../publishing-packet`,
123+
module: `${testRealmURL}publishing-packet`,
124124
name: 'PublishingPacket',
125125
},
126126
}),

packages/host/tests/integration/realm-indexing-and-querying-test.gts

+103
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,109 @@ module(`Integration | realm indexing and querying`, function (hooks) {
706706
}
707707
});
708708

709+
test('absolute urls will be serialised into relative into relative code-ref fields', async function (assert) {
710+
class Person extends CardDef {
711+
@field firstName = contains(StringField);
712+
}
713+
714+
let { realm, adapter } = await setupIntegrationTestRealm({
715+
loader,
716+
contents: {
717+
'person.gts': { Person },
718+
'person-spec.json': {
719+
data: {
720+
attributes: {
721+
title: 'Person Card',
722+
description: 'Spec for Person card',
723+
specType: 'card',
724+
ref: {
725+
module: `${testRealmURL}person`,
726+
name: 'Person',
727+
},
728+
},
729+
meta: {
730+
adoptsFrom: {
731+
module: 'https://cardstack.com/base/spec',
732+
name: 'Spec',
733+
},
734+
},
735+
},
736+
},
737+
},
738+
});
739+
let indexer = realm.realmIndexQueryEngine;
740+
let entry = await indexer.cardDocument(
741+
new URL(`${testRealmURL}person-spec`),
742+
);
743+
if (entry?.type === 'doc') {
744+
assert.deepEqual(entry.doc.data, {
745+
id: `${testRealmURL}person-spec`,
746+
type: 'card',
747+
links: {
748+
self: `${testRealmURL}person-spec`,
749+
},
750+
attributes: {
751+
title: 'Person Card',
752+
description: 'Spec for Person card',
753+
moduleHref: `${testRealmURL}person`,
754+
name: null,
755+
readMe: null,
756+
specType: 'card',
757+
isCard: true,
758+
isField: false,
759+
thumbnailURL: null,
760+
ref: {
761+
module: `./person`,
762+
name: 'Person',
763+
},
764+
containedExamples: [],
765+
},
766+
relationships: {
767+
linkedExamples: {
768+
links: {
769+
self: null,
770+
},
771+
},
772+
},
773+
meta: {
774+
adoptsFrom: {
775+
module: 'https://cardstack.com/base/spec',
776+
name: 'Spec',
777+
},
778+
lastModified: adapter.lastModifiedMap.get(
779+
`${testRealmURL}person-spec.json`,
780+
),
781+
resourceCreatedAt: adapter.resourceCreatedAtMap.get(
782+
`${testRealmURL}person-spec.json`,
783+
),
784+
realmInfo: testRealmInfo,
785+
realmURL: testRealmURL,
786+
},
787+
});
788+
let instance = await indexer.instance(
789+
new URL(`${testRealmURL}person-spec`),
790+
);
791+
assert.deepEqual(instance?.searchDoc, {
792+
_cardType: 'Spec',
793+
description: 'Spec for Person card',
794+
id: `${testRealmURL}person-spec`,
795+
specType: 'card',
796+
moduleHref: `${testRealmURL}person`,
797+
ref: `${testRealmURL}person/Person`,
798+
title: 'Person Card',
799+
linkedExamples: null,
800+
containedExamples: null,
801+
isCard: true,
802+
isField: false,
803+
});
804+
} else {
805+
assert.ok(
806+
false,
807+
`search entry was an error: ${entry?.error.errorDetail.message}`,
808+
);
809+
}
810+
});
811+
709812
test('can recover from rendering a card that has a template error', async function (assert) {
710813
{
711814
class Person extends CardDef {

0 commit comments

Comments
 (0)