Skip to content

Commit 0ffee51

Browse files
committed
feat(#14): Refactored create asset
- Used a more effective approach to create assets - Logged any errors that might occur - Simplified the error logging for update asset
1 parent e5cdec7 commit 0ffee51

File tree

5 files changed

+89
-76
lines changed

5 files changed

+89
-76
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,41 @@
11
import { dataSource } from "../../../../dataSource.js";
2+
import { createAsset as createAssetHelper } from "../../../../helpers/asset.js";
3+
import { withTransaction } from "../../../../scopes/index.js";
24
import {
3-
type Asset,
4-
AssetEntity,
5-
type Document,
6-
DocumentEntity,
7-
FileEntity,
8-
type Image,
9-
ImageEntity,
10-
} from "../../../../entities/index.js";
11-
import type { MutationResolvers } from "./../../../types.generated.js";
12-
import type { Maybe } from "@app/common";
13-
import { Decimal } from "decimal.js";
5+
DataSourceService,
6+
EntityManagerService,
7+
} from "../../../../services/index.js";
8+
import type {
9+
MutationResolvers,
10+
ResolversTypes,
11+
} from "./../../../types.generated.js";
12+
import { Effect, pipe } from "effect";
1413

1514
export const createAsset: NonNullable<
1615
MutationResolvers["createAsset"]
1716
> = async (_parent, { data }, _ctx) => {
18-
const asset = await dataSource.transaction<Asset>(async (manager) => {
19-
const asset = await manager.save(AssetEntity, {
20-
name: data.name,
21-
description: data.description,
22-
images: [],
23-
});
17+
const program = pipe(
18+
createAssetHelper(data),
19+
Effect.provideServiceEffect(EntityManagerService, withTransaction),
20+
Effect.provideService(DataSourceService, dataSource),
21+
Effect.scoped,
22+
Effect.andThen(
23+
(asset) =>
24+
({
25+
...asset,
26+
__typename: "Asset",
27+
}) as ResolversTypes["AssetResponse"],
28+
),
29+
Effect.catchAllCause((cause) =>
30+
pipe(
31+
Effect.logError("Failed to create asset", cause),
32+
Effect.as({
33+
__typename: "AssetError",
34+
message: "Failed to create asset",
35+
} as ResolversTypes["AssetResponse"]),
36+
),
37+
),
38+
);
2439

25-
const images: Image[] = data.images
26-
? await manager.save(
27-
ImageEntity,
28-
await Promise.all(
29-
data.images.map(async (image, index) => ({
30-
...image,
31-
asset,
32-
position: new Decimal(index),
33-
file: await manager.save(FileEntity, image.file),
34-
})),
35-
),
36-
)
37-
: [];
38-
39-
const proofOfPurchase: Maybe<Document> =
40-
data.proofOfPurchase &&
41-
(await manager.save(DocumentEntity, {
42-
...data.proofOfPurchase,
43-
asset,
44-
file: await manager.save(FileEntity, data.proofOfPurchase.file),
45-
}));
46-
47-
return {
48-
...asset,
49-
proofOfPurchase,
50-
images,
51-
} satisfies Asset;
52-
});
53-
54-
return {
55-
...asset,
56-
__typename: "Asset",
57-
};
40+
return Effect.runPromise(program);
5841
};

workspaces/backend/gql/src/gql-server/asset/resolvers/Mutation/updateAsset.ts

-11
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,6 @@ export const updateAsset: NonNullable<
3232
__typename: "Asset",
3333
}) as ResolversTypes["AssetResponse"],
3434
),
35-
Effect.catchIf(
36-
(error) => "_tag" in error,
37-
(error) =>
38-
pipe(
39-
Effect.logWarning("Failed to update asset", error),
40-
Effect.as({
41-
__typename: "AssetError",
42-
message: error.message,
43-
} as ResolversTypes["AssetResponse"]),
44-
),
45-
),
4635
Effect.catchAllCause((cause) =>
4736
pipe(
4837
Effect.logError("Failed to update asset", cause),

workspaces/backend/gql/src/helpers/asset.ts

+51-13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import { type Asset, AssetEntity } from "../entities/index.js";
22
import { ReadAssetError } from "../errors/ReadAssetError.js";
33
import { SaveAssetError } from "../errors/SaveAssetError.js";
4-
import type { UpdateAssetInput } from "../gql-server/types.generated.js";
4+
import type {
5+
CreateAssetInput,
6+
UpdateAssetInput,
7+
} from "../gql-server/types.generated.js";
58
import { EntityManagerService } from "../services/index.js";
6-
import { mutateImages } from "./image.js";
7-
import { mutateProofOfPurchase } from "./proofOfPurchase.js";
9+
import { createImage, mutateImages } from "./image.js";
10+
import {
11+
mutateProofOfPurchase,
12+
updateProofOfPurchase,
13+
} from "./proofOfPurchase.js";
814
import { Array, Effect, Option, pipe } from "effect";
915
import type { FindOptionsRelations } from "typeorm";
1016

@@ -36,22 +42,54 @@ export const readAsset = (
3642
});
3743
});
3844

45+
const saveAsset = (input: Partial<Asset>) =>
46+
Effect.gen(function* () {
47+
const manager = yield* EntityManagerService;
48+
49+
return yield* Effect.tryPromise({
50+
try: async () => (await manager.save(AssetEntity, input)) as Asset,
51+
catch: (cause) =>
52+
new SaveAssetError({
53+
message: "Failed to save asset",
54+
options: { cause, input },
55+
}),
56+
});
57+
});
58+
59+
export const createAsset = (input: CreateAssetInput) =>
60+
pipe(
61+
saveAsset({
62+
name: input.name,
63+
description: input.description,
64+
}),
65+
Effect.andThen((asset) =>
66+
Option.match(Option.fromNullable(input.proofOfPurchase), {
67+
onSome: (proofOfPurchase) =>
68+
updateProofOfPurchase(asset, proofOfPurchase),
69+
onNone: () => Effect.succeed(asset),
70+
}),
71+
),
72+
Effect.andThen((asset) =>
73+
Option.match(
74+
Option.filter(Option.fromNullable(input.images), Array.isNonEmptyArray),
75+
{
76+
onSome: (images) =>
77+
Effect.reduce(Array.reverse(images), asset, (asset, image) =>
78+
createImage(asset, image),
79+
),
80+
onNone: () => Effect.succeed(asset),
81+
},
82+
),
83+
),
84+
);
85+
3986
export const updateAsset = (asset: Asset, input: UpdateAssetInput) =>
4087
pipe(
4188
Effect.gen(function* () {
42-
const manager = yield* EntityManagerService;
43-
4489
if (input.name != null) asset.name = input.name;
4590
if (input.description != null) asset.description = input.description;
4691

47-
return yield* Effect.tryPromise({
48-
try: async () => await manager.save(AssetEntity, asset),
49-
catch: (cause) =>
50-
new SaveAssetError({
51-
message: "Failed to save document",
52-
options: { cause, input },
53-
}),
54-
});
92+
return yield* saveAsset(asset);
5593
}),
5694
Effect.andThen((asset) =>
5795
Option.match(Option.fromNullable(input.proofOfPurchase), {

workspaces/backend/gql/src/helpers/image.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const deleteImage = (input: Image) =>
5656
yield* deleteFile(input.file);
5757
});
5858

59-
const createImage = (asset: Asset, input: CreateImageInput) =>
59+
export const createImage = (asset: Asset, input: CreateImageInput) =>
6060
Effect.gen(function* () {
6161
const image = yield* saveImage({
6262
asset,

workspaces/backend/gql/src/helpers/proofOfPurchase.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ export const mutateProofOfPurchase = (
1818
return yield* deleteProofOfPurchase(asset, input.delete);
1919
});
2020

21-
const updateProofOfPurchase = (asset: Asset, input: UpdateDocumentInput) =>
21+
export const updateProofOfPurchase = (
22+
asset: Asset,
23+
input: UpdateDocumentInput,
24+
) =>
2225
Effect.gen(function* () {
2326
if (asset.proofOfPurchase) yield* deleteDocument(asset.proofOfPurchase);
2427

0 commit comments

Comments
 (0)