Skip to content

Commit

Permalink
fix: create a copy for CODAP v3
Browse files Browse the repository at this point in the history
  • Loading branch information
kswenson committed Feb 20, 2024
1 parent 42eecfe commit 185484c
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 13 deletions.
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ export interface CFMAppOptions {
// true if the application sets the page title
// false (default) if CFM sets page title from document name
appSetsWindowTitle?: boolean
// true if the content stored to file/disk should be wrapped with CFM metadata
// false if the content should be unwrapped before storing to file/disk
wrapFileContent?: boolean
mimeType?: string
// note different capitalization from CFMBaseProviderOptions
Expand Down
2 changes: 1 addition & 1 deletion src/code/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class CloudFileManagerClient {
this.appOptions = appOptions
if (this.appOptions.wrapFileContent == null) { this.appOptions.wrapFileContent = true }
CloudContent.wrapFileContent = this.appOptions.wrapFileContent
if (this.appOptions.isClientContent) cloudContentFactory.isClientContent = this.appOptions.isClientContent
if (this.appOptions.isClientContent) CloudContent.isClientContent = this.appOptions.isClientContent

type ProviderClass = any
const allProviders: Record<string, ProviderClass> = {}
Expand Down
35 changes: 35 additions & 0 deletions src/code/providers/provider-interface.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { CloudContent, cloudContentFactory } from "./provider-interface"

describe("ProviderInterface", () => {

it("wraps/unwraps client content with `metadata` property (e.g. CODAP v2)", () => {
const docContent = { metadata: {} }
expect(CloudContent.isClientContent(docContent)).toBe(true)
const wrappedContent = cloudContentFactory.createEnvelopedCloudContent(docContent)
const unwrappedContent = wrappedContent.getClientContent()
expect(unwrappedContent).toEqual(docContent)
expect(CloudContent.isClientContent(unwrappedContent)).toBe(true)
})

it("can't wrap/unwrap client content with `content` property (e.g. CODAP v3) without isClientContent", () => {
const docContent = { content: { isContent: true } }
const wrappedContent = cloudContentFactory.createEnvelopedCloudContent(docContent)
const unwrappedContentFail = wrappedContent.getClientContent()
// without the isClientContent override, unwrapping fails
expect(unwrappedContentFail).not.toEqual(docContent)
})

it("wraps/unwraps client content with `content` property (e.g. CODAP v3) with isClientContent", () => {
const docContent = { content: { isContent: true } }
// with the isClientContent override, unwrapping succeeds
CloudContent.isClientContent = (inContent: any) => !!inContent?.content?.isContent
expect(CloudContent.isClientContent(docContent)).toBe(true)
const wrappedContent = cloudContentFactory.createEnvelopedCloudContent(docContent)
console.log(wrappedContent)
const unwrappedContent = wrappedContent.getClientContent()
console.log(unwrappedContent)
expect(CloudContent.isClientContent(unwrappedContent)).toBe(true)
expect(unwrappedContent).toEqual(docContent)
})

})
35 changes: 23 additions & 12 deletions src/code/providers/provider-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,8 @@ interface IEnvelopeMetaData {

// singleton that can create CloudContent wrapped with global options
class CloudContentFactory {
// For backward compatibility, by default we assume that a top-level `metadata`
// property indicates an unwrapped client document (e.g. CODAP v2). Clients can
// override this assumption with the `isClientContent` configuration option.
isClientContent = (content: unknown) => {
return typeof content === "object" && "metadata" in content && !!content.metadata
}

envelopeMetadata: IEnvelopeMetaData

constructor() {
this.envelopeMetadata = {
// replaced by version number at build time
Expand Down Expand Up @@ -215,7 +209,7 @@ class CloudContentFactory {
}
}
// If looks like client content, then it's neither wrapped nor pre-CFM.
if (this.isClientContent(content)) {
if (CloudContent.isClientContent(content)) {
return result
}
if (
Expand All @@ -237,7 +231,7 @@ class CloudContentFactory {
// noop, just checking if it's json or plain text
}
}
if ((typeof content === "object") && (content?.content != null)) {
if ((typeof content === "object") && (content?.content != null) && !CloudContent.isClientContent(content)) {
return content
} else {
return {content}
Expand All @@ -251,8 +245,18 @@ export interface CloudContentFormat {
}

class CloudContent {
// Client content is always wrapped by the CFM while it is being handled internally.
// This setting controls whether content is stored to file/disk in its wrapped form
// or whether it should be unwrapped before serializing to file/disk.
static wrapFileContent: boolean = true

// For backward compatibility, by default we assume that a top-level `metadata`
// property indicates an unwrapped client document (e.g. CODAP v2). Clients can
// override this assumption with the `isClientContent` configuration option.
static isClientContent = (content: unknown) => {
return typeof content === "object" && "metadata" in content && !!content.metadata
}

// TODO: These should probably be private, but there is some refactoring
// that has to happen to make this possible
cfmVersion?: string
Expand All @@ -264,7 +268,8 @@ class CloudContent {
this.contentFormat = contentFormat
}

// getContent and getContentAsJSON return the file content as stored on disk
// getContent and getContentAsJSON return the file content as stored on disk.
// They are expected to be called on internally wrapped content.
getContent() {
return CloudContent.wrapFileContent
? this.content
Expand All @@ -275,9 +280,15 @@ class CloudContent {
return JSON.stringify(this.getContent())
}

// returns the client-visible content (excluding wrapper for wrapped clients)
// Returns the client-visible content (excluding wrapper).
// Note that this can be called with wrapped or unwrapped content independent of the `wrapFileContent`
// setting, because CFM wraps content internally, so we need to inspect the content.
getClientContent() {
return CloudContent.wrapFileContent
// if we can specifically identify client content, then return it
if (CloudContent.isClientContent(this.content?.content)) return this.content.content
if (CloudContent.isClientContent(this.content)) return this.content
// otherwise, assume that a nested `content` property means we are wrapped
return this.content?.content
? this.content.content
: this.content
}
Expand Down

0 comments on commit 185484c

Please sign in to comment.