Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat (remote assets): handle asset pack containing asset overrides #2408

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions packages/data-models/assets.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// TODO - can likely refactor to here instead and refactor other imports
import type { IContentsEntry } from "shared";

/** Extend to include fields for front-end features */
interface IAssetContentsEntry extends IContentsEntry {
/**
* Stores one of the following:
* 1. For core assets: Specific path to file when not the same as relativePath, e.g. asset overrides
* 2. For remote assets, on native devices: The path to the local file in native storage
* 3. For remote assets, on web: The public URL for the remotely hosted file (in supabase storage)
* */
filePath?: string;
}

export type IAssetContentsEntryMinimal = Omit<IAssetContentsEntry, "relativePath" | "modifiedTime">;

export interface IAssetOverrideProps {
themeName: string;
languageCode: string;
}

export interface IAssetEntry extends IAssetContentsEntryMinimal {
/** id field is required to convert asset contents to and from data_list format */
id?: string;
/** Used to indicate that the asset pack contains only overrides for the associated file, not the default asset file */
overridesOnly?: boolean;
overrides?: {
[themeName: IAssetOverrideProps["themeName"]]: {
[languageCode: IAssetOverrideProps["languageCode"]]: IAssetContentsEntryMinimal;
};
};
}
export type IAssetEntryHashmap = { [assetPath: string]: IAssetEntry };
49 changes: 9 additions & 40 deletions packages/data-models/deployment.model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { IContentsEntry } from "shared";
import type { IGdriveEntry } from "../@idemsInternational/gdrive-tools";
import type { IAppConfig, IAppConfigOverride } from "./appConfig";

Expand Down Expand Up @@ -150,6 +151,14 @@ interface IDeploymentCoreConfig {
_parent_config?: Partial<IDeploymentConfig & { _workspace_path: string }>;
}

/** Duplicate type defintion from data-models (TODO - find better way to share) */
interface IFlowTypeBase {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved from lower down to group with other relevant definitions

flow_type: string;
flow_name: string;
flow_subtype?: string;
status: "draft" | "released";
}

export type IDeploymentConfig = IDeploymentCoreConfig & IDeploymentRuntimeConfig;

/**
Expand Down Expand Up @@ -230,43 +239,3 @@ export const DEPLOYMENT_CONFIG_DEFAULTS: IDeploymentConfig = {
_parent_config: null,
_version: 1.0,
};

/** Duplicate type defintion from scripts (TODO - find better way to share) */
interface IContentsEntry {
relativePath: string;
size_kb: number;
modifiedTime: string;
md5Checksum: string;
}

/** Extend to include fields for front-end features */
interface IAssetContentsEntry extends IContentsEntry {
/**
* Stores one of the following:
* 1. For core assets: Specific path to file when not the same as relativePath, e.g. asset overrides
* 2. For remote assets, on native devices: The path to the local file in native storage
* 3. For remote assets, on web: The public URL for the remotely hosted file (in supabase storage)
* */
filePath?: string;
/** id field is required to convert asset contents to and from data_list format */
id?: string;
}

/** Duplicate type defintion from data-models (TODO - find better way to share) */
interface IFlowTypeBase {
flow_type: string;
flow_name: string;
flow_subtype?: string;
status: "draft" | "released";
}

export type IAssetContentsEntryMinimal = Omit<IAssetContentsEntry, "relativePath" | "modifiedTime">;

export interface IAssetEntry extends IAssetContentsEntryMinimal {
overrides?: {
[theme_name: string]: {
[language_code: string]: IAssetContentsEntryMinimal;
};
};
}
export type IAssetEntryHashmap = { [assetPath: string]: IAssetEntry };
2 changes: 1 addition & 1 deletion packages/data-models/flowTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import type { IDataPipeOperation } from "shared";
import type { IAppConfig } from "./appConfig";
import type { IAssetEntry } from "./deployment.model";
import type { IAssetEntry } from "./assets.model";

/*********************************************************************************************
* Base flow types
Expand Down
1 change: 1 addition & 0 deletions packages/data-models/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./appConfig";
export * from "./assets.model";
export * from "./db.model";
export * from "./deployment.model";
export * from "./fields";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { vol } from "memfs";
// Use default imports to allow spying on functions and replacing with mock methods
import { ActiveDeployment } from "../../deployment/get";
import { resolve } from "path";
import { IAssetEntryHashmap } from "data-models/deployment.model";
import { IAssetEntryHashmap } from "data-models/assets.model";
import { useMockLogger } from "../../../../test/helpers/utils";

// Mock all fs calls to use memfs implementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
copyFileWithTimestamp,
} from "../../../utils";
import { ActiveDeployment } from "../../deployment/get";
import type { IAssetEntryHashmap, IAssetContentsEntryMinimal } from "data-models/deployment.model";
import type { IAssetEntryHashmap, IAssetContentsEntryMinimal } from "data-models";
import { resolve } from "path";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
.popup-container {
height: var(--safe-area-height);
}
.close-button {
.close-button {
Copy link
Collaborator Author

@jfmcquade jfmcquade Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this is cropping up now, but reverting it and committing isn't possible as the linter removes the whitespace again when I attempt a commit (so the would-be commit is empty)

top: 10px;
right: 10px;
}
Expand Down
160 changes: 145 additions & 15 deletions src/app/shared/services/remote-asset/remote-asset.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,105 @@
import { TestBed } from "@angular/core/testing";

import { RemoteAssetService } from "./remote-asset.service";
import { MockDeploymentService } from "../deployment/deployment.service.spec";
import { HttpClientTestingModule } from "@angular/common/http/testing";
import { IAssetContents } from "src/app/data";
import { FlowTypes } from "../../model";
import { IAssetEntry, IDeploymentRuntimeConfig } from "data-models";
import clone from "clone";
import { arrayToHashmap } from "../../utils";
import { DeploymentService } from "../deployment/deployment.service";

const MOCK_ASSETS_CONTENTS_LIST: IAssetContents = {
"audio/test_audio.mp3": {
"images/asset.png": {
md5Checksum: "b4e6f1e9ba6e5bcdd2e404bc432ba745",
size_kb: 100,
},
"audio/asset_with_overrides.mp3": {
md5Checksum: "5ddddf934d2187d084c75b7e27797fae",
size_kb: 43.4,
overrides: {
theme_default: {
tz_sw: {
filePath: "tz_sw/audio/asset_with_overrides.mp3",
md5Checksum: "d851eef52c8d12fdbf0497210961a407",
size_kb: 21.6,
},
},
},
},
"audio/asset_override_only.mp3": {
md5Checksum: "5ddddf934d2187d084c75b7e27797hol",
size_kb: 42.4,
overrides: {
theme_default: {
tz_sw: {
filePath: "tz_sw/audio/asset_override_only.mp3",
md5Checksum: "5ddddf934d2187d084c75b7e27797hol",
size_kb: 42.4,
},
},
},
overridesOnly: true,
},
};

const MOCK_ASSET_ENTRY: IAssetEntry = {
id: "images/asset.png",
md5Checksum: "b4e6f1e9ba6e5bcdd2e404bc432ba745",
size_kb: 100,
};
const MOCK_ASSET_ENTRY_WITH_OVERRIDES: IAssetEntry = {
id: "audio/asset_with_overrides.mp3",
md5Checksum: "5ddddf934d2187d084c75b7e27797fae",
size_kb: 43.4,
overrides: {
theme_default: {
tz_sw: {
filePath: "tz_sw/audio/asset_with_overrides.mp3",
md5Checksum: "d851eef52c8d12fdbf0497210961a407",
size_kb: 21.6,
},
},
},
};
const MOCK_ASSET_ENTRY_OVERRIDES_ONLY: IAssetEntry = {
id: "audio/asset_override_only.mp3",
md5Checksum: "5ddddf934d2187d084c75b7e27797hol",
size_kb: 42.4,
overrides: {
theme_default: {
tz_sw: {
filePath: "tz_sw/audio/asset_override_only.mp3",
md5Checksum: "5ddddf934d2187d084c75b7e27797hol",
size_kb: 42.4,
},
},
},
overridesOnly: true,
};

const MOCK_CORE_ASSET_PACK_ROWS: FlowTypes.Data_listRow<IAssetEntry>[] = [
clone(MOCK_ASSET_ENTRY) as FlowTypes.Data_listRow<IAssetEntry>,
clone(MOCK_ASSET_ENTRY_WITH_OVERRIDES) as FlowTypes.Data_listRow<IAssetEntry>,
clone(MOCK_ASSET_ENTRY_OVERRIDES_ONLY) as FlowTypes.Data_listRow<IAssetEntry>,
];

const MOCK_CORE_ASSET_PACK_ROWS_HASHMAP: Record<
string,
FlowTypes.Data_listRow<IAssetEntry>
> = arrayToHashmap(MOCK_CORE_ASSET_PACK_ROWS, "id");

const MOCK_CORE_ASSET_PACK: FlowTypes.AssetPack = {
flow_type: "asset_pack",
flow_name: "core_assets",
rows: [
{
id: "audio/test_audio.mp3",
md5Checksum: "5ddddf934d2187d084c75b7e27797fae",
size_kb: 43.4,
},
],
rowsHashmap: {
"audio/test_audio.mp3": {
id: "audio/test_audio.mp3",
md5Checksum: "5ddddf934d2187d084c75b7e27797fae",
size_kb: 43.4,
},
rows: MOCK_CORE_ASSET_PACK_ROWS,
rowsHashmap: MOCK_CORE_ASSET_PACK_ROWS_HASHMAP,
};

const MOCK_DEPLOYMENT_CONFIG: Partial<IDeploymentRuntimeConfig> = {
name: "MOCK",
supabase: {
enabled: true,
},
};

Expand All @@ -41,6 +113,9 @@ describe("RemoteAssetsService", () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
{ provide: DeploymentService, useValue: new MockDeploymentService(MOCK_DEPLOYMENT_CONFIG) },
],
});
service = TestBed.inject(RemoteAssetService);
});
Expand All @@ -53,4 +128,59 @@ describe("RemoteAssetsService", () => {
const coreAssetPack = service["generateCoreAssetPack"](MOCK_ASSETS_CONTENTS_LIST);
expect(coreAssetPack).toEqual(MOCK_CORE_ASSET_PACK);
});

it("adds filepath to asset entry for asset without overrides", () => {
const assetEntryWithFilePath = service["addFilePathToAssetEntry"](
MOCK_ASSET_ENTRY,
"new/path/to/asset.png"
);
expect(assetEntryWithFilePath).toEqual({
...MOCK_ASSET_ENTRY,
filePath: "new/path/to/asset.png",
});
});

it("adds filepath to asset entry for asset with overrides", () => {
const assetEntryWithOverrideWithFilePath = service["addFilePathToAssetEntry"](
MOCK_ASSET_ENTRY_WITH_OVERRIDES,
"new/path/to/asset_with_overrides.mp3",
{ themeName: "theme_default", languageCode: "tz_sw" }
);
expect(assetEntryWithOverrideWithFilePath).toEqual({
id: "audio/asset_with_overrides.mp3",
md5Checksum: "5ddddf934d2187d084c75b7e27797fae",
size_kb: 43.4,
overrides: {
theme_default: {
tz_sw: {
filePath: "new/path/to/asset_with_overrides.mp3",
md5Checksum: "d851eef52c8d12fdbf0497210961a407",
size_kb: 21.6,
},
},
},
});
});

it("adds filepath to asset entry for asset that is solely an override", () => {
const assetEntryWithOverrideWithFilePath = service["addFilePathToAssetEntry"](
MOCK_ASSET_ENTRY_WITH_OVERRIDES,
"new/path/to/asset_with_overrides.mp3",
{ themeName: "theme_default", languageCode: "tz_sw" }
);
expect(assetEntryWithOverrideWithFilePath).toEqual({
id: "audio/asset_with_overrides.mp3",
md5Checksum: "5ddddf934d2187d084c75b7e27797fae",
size_kb: 43.4,
overrides: {
theme_default: {
tz_sw: {
filePath: "new/path/to/asset_with_overrides.mp3",
md5Checksum: "d851eef52c8d12fdbf0497210961a407",
size_kb: 21.6,
},
},
},
});
});
});
Loading
Loading