Skip to content
This repository was archived by the owner on Aug 15, 2023. It is now read-only.

Commit e1f3075

Browse files
committed
fix(tk:shared): defined metadata filters based on nature type
1 parent e3e383c commit e1f3075

File tree

20 files changed

+394
-142
lines changed

20 files changed

+394
-142
lines changed

Diff for: packages/shared/src/endpoints/MinimalEndpoint.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ export interface TypeOfEndpointInstance<E extends MinimalEndpointInstance> {
2222
? // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
2323
void
2424
: {
25-
[k in keyof NonNullable<E['Input']>]: NonNullable<
25+
[K in keyof NonNullable<E['Input']>]: NonNullable<
2626
E['Input']
27-
>[k] extends Codec<any, any, any>
28-
? runtimeType<NonNullable<E['Input']>[k]>
27+
>[K] extends Codec<any, any, any>
28+
? runtimeType<NonNullable<E['Input']>[K]>
2929
: never;
3030
};
3131
}

Diff for: platforms/tktrex/backend/io/metadata.io.ts

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { APIError, toAPIError } from '@shared/errors/APIError';
2+
import { toValidationError } from '@shared/errors/ValidationError';
3+
import { MetadataBase } from '@shared/models/MetadataBase';
4+
import { string2Food } from '@shared/utils/food.utils';
5+
import {
6+
FollowingVideoMetadata,
7+
ForYouMetadata,
8+
NativeMetadata,
9+
ProfileMetadata,
10+
SearchMetadata,
11+
TKMetadata,
12+
} from '@tktrex/shared/models/metadata';
13+
import {
14+
CreatorType,
15+
FollowingType,
16+
ForYouType,
17+
NativeType,
18+
ProfileType,
19+
SearchType,
20+
} from '@tktrex/shared/models/Nature';
21+
import * as E from 'fp-ts/Either';
22+
import { pipe } from 'fp-ts/function';
23+
import {
24+
FollowingVideoMetadataDB,
25+
ForYouVideoMetadataDB,
26+
NativeMetadataDB,
27+
ProfileMetadataDB,
28+
SearchMetadataDB,
29+
TKMetadataDB,
30+
} from '../models/metadata';
31+
32+
type SpecificM<M> = Omit<M, keyof Metadata | '_id' | 'publicKey'>;
33+
34+
export const toTKNativeMetadata = (
35+
{ author, ...m }: SpecificM<NativeMetadataDB>,
36+
meta: MetadataBase
37+
): NativeMetadata => {
38+
return {
39+
...m,
40+
...meta,
41+
author: author ?? undefined,
42+
};
43+
};
44+
45+
export const toProfileMetadata = (
46+
m: SpecificM<ProfileMetadataDB>,
47+
meta: MetadataBase
48+
): ProfileMetadata => {
49+
return {
50+
...m,
51+
...meta,
52+
};
53+
};
54+
55+
export const toForYouMetadata = (
56+
{ author, music, hashtags, ...m }: SpecificM<ForYouVideoMetadataDB>,
57+
meta: MetadataBase
58+
): ForYouMetadata => {
59+
return {
60+
...m,
61+
...meta,
62+
author: author ?? undefined,
63+
music: music ?? undefined,
64+
hashtags: hashtags ?? [],
65+
};
66+
};
67+
68+
export const toSearchMetadata = (
69+
m: SpecificM<SearchMetadataDB>,
70+
meta: MetadataBase
71+
): SearchMetadata => {
72+
return { ...m, ...meta };
73+
};
74+
75+
export const toFollowingMetadata = (
76+
m: SpecificM<FollowingVideoMetadataDB>,
77+
meta: MetadataBase
78+
): FollowingVideoMetadata => {
79+
return { ...m, ...meta };
80+
};
81+
82+
export const toTKMetadata = ({
83+
publicKey,
84+
_id,
85+
id,
86+
blang,
87+
href,
88+
savingTime,
89+
clientTime,
90+
experimentId,
91+
researchTag,
92+
...m
93+
}: TKMetadataDB): E.Either<APIError, TKMetadata> => {
94+
const meta: MetadataBase = {
95+
id: id.substring(0, 10),
96+
blang,
97+
href,
98+
supporter: string2Food(publicKey),
99+
savingTime,
100+
clientTime,
101+
researchTag,
102+
experimentId,
103+
};
104+
105+
return pipe(
106+
E.tryCatch(() => {
107+
const mm: any = m;
108+
switch (m.nature.type) {
109+
case SearchType.value: {
110+
return toSearchMetadata(mm, meta);
111+
}
112+
case ForYouType.value: {
113+
return toForYouMetadata(mm, meta);
114+
}
115+
case FollowingType.value: {
116+
return toFollowingMetadata(mm, meta);
117+
}
118+
case CreatorType.value:
119+
case ProfileType.value: {
120+
return toProfileMetadata(mm, meta);
121+
}
122+
case NativeType.value: {
123+
return toTKNativeMetadata(mm, meta);
124+
}
125+
}
126+
}, toAPIError),
127+
E.chain((e) =>
128+
pipe(
129+
TKMetadata.decode(e),
130+
E.mapLeft((e) => toValidationError(TKMetadata.name, e))
131+
)
132+
)
133+
);
134+
};
+8-8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import { ForYouVideoMetadata } from '@tktrex/shared/models/metadata/ForYouMetadata';
1+
import { ForYouMetadata } from '@tktrex/shared/models/metadata/ForYouMetadata';
22
import * as t from 'io-ts';
33

4-
const { supporter, ...metadataBaseProps } = ForYouVideoMetadata.types[0].props;
5-
export const ForYouVideoMetadataDB = t.strict(
4+
const { supporter, ...metadataBaseProps } = ForYouMetadata.types[0].props;
5+
export const ForYouMetadataDB = t.strict(
66
{
77
...metadataBaseProps,
8-
...ForYouVideoMetadata.types[1].type.props,
9-
...ForYouVideoMetadata.types[2].props,
10-
...ForYouVideoMetadata.types[3].props,
8+
...ForYouMetadata.types[1].type.props,
9+
...ForYouMetadata.types[2].props,
10+
...ForYouMetadata.types[3].props,
1111
_id: t.any,
1212
publicKey: t.string,
1313
},
14-
'ForYouVideoMetadataDB'
14+
'ForYouMetadataDB'
1515
);
16-
export type ForYouVideoMetadataDB = t.TypeOf<typeof ForYouVideoMetadataDB>;
16+
export type ForYouMetadataDB = t.TypeOf<typeof ForYouMetadataDB>;

Diff for: platforms/tktrex/backend/models/metadata/index.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as t from 'io-ts';
22
import { FollowingVideoMetadataDB } from './FollowingMetadata';
3-
import { ForYouVideoMetadataDB } from './ForYouMetadata';
3+
import { ForYouMetadataDB } from './ForYouMetadata';
44
import { NativeMetadataDB } from './NativeMetadata';
55
import { ProfileMetadataDB } from './ProfileMetadata';
66
import { SearchMetadataDB } from './SearchMetadata';
@@ -9,10 +9,18 @@ export const TKMetadataDB = t.union(
99
[
1010
NativeMetadataDB,
1111
SearchMetadataDB,
12-
ForYouVideoMetadataDB,
12+
ForYouMetadataDB,
1313
FollowingVideoMetadataDB,
1414
ProfileMetadataDB,
1515
],
1616
'MetadataDB'
1717
);
1818
export type TKMetadataDB = t.TypeOf<typeof TKMetadataDB>;
19+
20+
export {
21+
ProfileMetadataDB,
22+
NativeMetadataDB,
23+
SearchMetadataDB,
24+
FollowingVideoMetadataDB,
25+
ForYouMetadataDB as ForYouVideoMetadataDB,
26+
};

Diff for: platforms/tktrex/backend/routes/metadata.ts

+106-33
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,51 @@
1+
import { toAPIError } from '@shared/errors/APIError';
12
import { decodeOrThrowRequest } from '@shared/endpoints/helper';
2-
import * as foodUtils from '@shared/utils/food.utils';
3-
import endpoints, {
4-
ListMetadataResponse,
5-
} from '@tktrex/shared/endpoints/v2/metadata.endpoints';
3+
import endpoints from '@tktrex/shared/endpoints/v2/metadata.endpoints';
64
import createDebug from 'debug';
5+
import * as A from 'fp-ts/Array';
6+
import { pipe } from 'fp-ts/lib/function';
7+
import * as TE from 'fp-ts/TaskEither';
8+
import * as E from 'fp-ts/lib/Either';
9+
import { toTKMetadata } from '../io/metadata.io';
710
import _ from 'lodash';
811
import * as automo from '../lib/automo';
12+
import { throwTE } from '@shared/utils/task.utils';
13+
import { AppError } from '@shared/errors/AppError';
14+
import moment from 'moment';
15+
import CSV from '../lib/CSV';
16+
import { ListMetadataOutput } from '@tktrex/shared/models/http/metadata/output/ListMetadata.output';
17+
import { ListMetadataQuery } from '@tktrex/shared/models/http/metadata/query/ListMetadata.query';
918

1019
const debug = createDebug('routes:public');
1120

1221
// This variables is used as cap in every readLimit below
1322
const PUBLIC_AMOUNT_ELEMS = 100;
1423

15-
const listMetadata = async (
16-
req: any
17-
): Promise<{ json: ListMetadataResponse }> => {
24+
type ListMetadataResponse =
25+
| { json: ListMetadataOutput }
26+
| { headers: any; text: string };
27+
28+
const listMetadata = async (req: any): Promise<ListMetadataResponse> => {
29+
const { query } = decodeOrThrowRequest(
30+
endpoints.ListMetadata,
31+
req
32+
) as any as {
33+
query: ListMetadataQuery;
34+
};
35+
36+
debug('Filter metadata with query %O', query);
37+
1838
const {
19-
query: {
20-
researchTag,
21-
experimentId,
22-
publicKey,
23-
nature,
24-
amount = PUBLIC_AMOUNT_ELEMS,
25-
skip = 0,
26-
},
27-
} = decodeOrThrowRequest(endpoints.ListMetadata, req);
39+
researchTag,
40+
experimentId,
41+
publicKey,
42+
nature,
43+
amount = PUBLIC_AMOUNT_ELEMS,
44+
skip = 0,
45+
format,
46+
} = query;
47+
48+
debug('Filter metadata with query %O', query);
2849

2950
const filter = {} as any;
3051
if (publicKey) {
@@ -36,29 +57,81 @@ const listMetadata = async (
3657
if (researchTag) {
3758
filter.researchTag = researchTag;
3859
}
60+
3961
if (nature) {
4062
filter['nature.type'] = nature;
63+
switch (nature) {
64+
case 'search': {
65+
const { query: q } = query;
66+
if (q) {
67+
filter.query = {
68+
$regex: q,
69+
};
70+
}
71+
break;
72+
}
73+
case 'native': {
74+
const { description } = query;
75+
76+
if (description) {
77+
filter.description = {
78+
$regexp: description,
79+
};
80+
}
81+
break;
82+
}
83+
}
4184
}
4285

4386
debug('Filtering metadata for %O (%d, %d)', filter, amount, skip);
4487

45-
const metadata = await automo
46-
.getMetadataByFilter(filter, {
47-
amount,
48-
skip,
49-
})
50-
.then(({ totals, data }) => ({
51-
totals,
52-
data: data.map(({ publicKey, _id, id, ...m }) => ({
53-
...m,
54-
id: id.substring(0, 10),
55-
supporter: foodUtils.string2Food(publicKey),
56-
})),
57-
}));
58-
59-
debug('Fetched %d evidences of %d', _.size(metadata.data), metadata.totals);
60-
61-
return { json: metadata };
88+
return pipe(
89+
TE.tryCatch(
90+
() =>
91+
automo.getMetadataByFilter(filter, {
92+
amount,
93+
skip,
94+
}),
95+
toAPIError
96+
),
97+
TE.chain(({ totals, data }) => {
98+
debug('Metadata by %O, %d evidences', filter, _.size(data));
99+
return pipe(
100+
data.map(toTKMetadata),
101+
A.sequence(E.Applicative),
102+
E.map((d) => ({ data: d, totals })),
103+
TE.fromEither
104+
);
105+
}),
106+
TE.chain((metadata): TE.TaskEither<AppError, ListMetadataResponse> => {
107+
if (format === 'csv') {
108+
const csv = CSV.produceCSVv1(metadata.data);
109+
let filename = `metadata`;
110+
filename += experimentId ? `-experiment-${experimentId}` : '';
111+
filename += researchTag ? `-research_tag-${researchTag}` : '';
112+
filename += '-' + moment().format('YY-MM-DD') + '.csv';
113+
114+
debug(
115+
'VideoCSV: produced %d bytes, returning %s',
116+
_.size(csv),
117+
filename
118+
);
119+
120+
// if (!_.size(csv)) return { text: 'Error, Zorry: 🤷' };
121+
122+
return TE.right({
123+
headers: {
124+
'Content-Type': 'csv/text',
125+
'Content-Disposition': `attachment; filename=${filename}`,
126+
},
127+
text: csv,
128+
});
129+
}
130+
131+
return TE.right({ json: metadata });
132+
}),
133+
throwTE
134+
);
62135
};
63136

64137
export { listMetadata };

Diff for: platforms/tktrex/backend/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"include": [
2929
"./bin",
3030
"./lib",
31+
"./io",
3132
"./models",
3233
"./routes",
3334
"./test",

0 commit comments

Comments
 (0)