fix(api-profile-filter): Fix several bugs filtering data in profile requests

profile-routes-options: Profile-related routes were not properly instantiating/passing a ProfileOptions object - this has now been fixed.
profile-options: Some of the logic for the ProfileOptions class was rewritten/reorganised for clarity and stability.
profile-v2: Because some fields were nullish, the conversion between v2 and v1 wasn't working properly. Now, everything checks for nullability AFAIK.
This commit is contained in:
tecc 2023-06-02 00:21:52 +02:00
parent 8bb0a7fde0
commit 5c68d11b80
No known key found for this signature in database
GPG Key ID: 400AAD881FCC028B
2 changed files with 65 additions and 59 deletions

View File

@ -7,12 +7,14 @@ const opinions = {
}
const upgradeOpinionsListToV2 = (opinionsListV1) => {
if (opinionsListV1 == null) return undefined;
return Object.entries(opinionsListV1).map(([value, opinion]) => {
return { value, opinion: Object.keys(opinions).find(key => opinions[key] === opinion) };
});
}
const downgradeOpinionsListToV1 = (opinionsListV2) => {
if (opinionsListV2 == null) return undefined;
const opinionsListV1 = {};
for (let {value, opinion} of opinionsListV2) {
opinionsListV1[value] = opinions[opinion];
@ -21,12 +23,14 @@ const downgradeOpinionsListToV1 = (opinionsListV2) => {
}
const upgradeCustomFlagsListToV2 = (customFlagListV1) => {
if (customFlagListV1 == null) return undefined;
return Object.entries(customFlagListV1).map(([value, name]) => {
return { value, name };
});
}
const downgradeCustomFlagsToV1 = (customFlagListV2) => {
if (customFlagListV2 == null) return undefined;
const customFlagListV1 = {};
for (let {value, name} of customFlagListV2) {
customFlagListV1[value] = name;
@ -39,7 +43,7 @@ module.exports.upgradeToV2 = (profileV1) => {
...profileV1,
names: upgradeOpinionsListToV2(profileV1.names),
pronouns: upgradeOpinionsListToV2(profileV1.pronouns),
words: profileV1.words.map(column => {
words: profileV1.words && profileV1.words.map(column => {
return {'header': null, 'values': upgradeOpinionsListToV2(column)};
}),
customFlags: upgradeCustomFlagsListToV2(profileV1.customFlags),
@ -51,7 +55,7 @@ module.exports.downgradeToV1 = (profileV2) => {
...profileV2,
names: downgradeOpinionsListToV1(profileV2.names),
pronouns: downgradeOpinionsListToV1(profileV2.pronouns),
words: profileV2.words.map(({values}) => {
words: profileV2.words && profileV2.words.map(({values}) => {
return downgradeOpinionsListToV1(values);
}),
customFlags: downgradeCustomFlagsToV1(profileV2.customFlags),

View File

@ -72,10 +72,7 @@ class ProfileOptions {
enabled: true,
default_value: true
};
locale_specific_props = {
enabled: true,
default_value: true
};
locale_specific_props = {};
constructor(query_obj, locales) {
if (query_obj == null) return;
@ -84,13 +81,14 @@ class ProfileOptions {
this.default_props = ProfileOptions._parse_props(default_props)
if (locales != null) {
for (const key of Object.keys(locales)) {
for (const locale of Object.values(locales)) {
const key = locale.code.toLowerCase(); // i'm purely paranoid here
const lprops_obj = query_obj["lprops"];
let props;
if (typeof lprops_obj === "object") {
props = lprops_obj[key];
} else {
props = query_obj[`lprops.${key.toLowerCase()}`];
props = query_obj[`lprops.${key}`];
}
if (props != null) {
this.locale_specific_props[key] = ProfileOptions._parse_props(props);
@ -102,62 +100,65 @@ class ProfileOptions {
}
static _parse_props(raw_param) {
if (raw_param) {
/** @type {Array<string>} */
let properties;
let default_value = false;
const obj = {
default_value
};
if (Array.isArray(raw_param)) {
properties = raw_param
} else if (typeof raw_param == "object") {
return {
...raw_param,
enabled: true,
default_value: false,
}
} else if (typeof raw_param === 'string') {
if (raw_param === "none") {
// Don't show the locale at all
return {
enabled: false,
default_value: false
}
} else if (raw_param === "empty") {
// Include it as an object, but don't include any data
return {
enabled: true,
default_value: false
}
} else if (raw_param === "all") {
// All properties should be shown
return {
enabled: true,
default_value: true
}
}
properties = raw_param.split(',');
}
for (const property of properties) {
obj[property] = !default_value;
}
return {
...obj,
enabled: true,
};
} else {
if (raw_param == null) {
return {
enabled: true,
default_value: true
}
}
/** @type {Array<string>} */
let properties;
let default_value = false;
const obj = {};
if (Array.isArray(raw_param)) {
properties = raw_param
} else if (typeof raw_param == "object") {
return {
...raw_param,
enabled: true,
default_value: false,
}
} else if (typeof raw_param === 'string') {
switch (raw_param) {
case "none":
// Don't show the locale at all
return {
enabled: false,
default_value: false
}
case "empty":
// Include it as an object, but don't include any data
return {
enabled: true,
default_value: false
}
case "all":
// All properties should be shown
return {
enabled: true,
default_value: true
}
}
// console.log("unrecognised: ", raw_param)
properties = raw_param.split(',');
}
for (const property of properties) {
obj[property] = !default_value;
}
return {
...obj,
enabled: true,
default_value
};
}
_data_for(locale) {
if (typeof locale != "string") {
throw new Error("Locale must be string")
}
return this.locale_specific_props[locale] ?? this.default_props;
}
@ -174,8 +175,10 @@ class ProfileOptions {
* @return {boolean}
*/
prop(locale, property) {
if (!this.show_at_all(locale)) return false;
// it's duplicate data getting (show_at_all also calls _data_for),
// but it's not that important to optimise
const data = this._data_for(locale);
if (!(data.enabled ?? true)) return false;
return data[property] ?? data.default_value ?? true;
}
@ -225,7 +228,7 @@ const fetchProfiles = async (db, username, self, opts) => {
const p = {}
for (let profile of profiles) {
if (!opts.show_at_all(profile)) continue;
if (!opts.show_at_all(profile.locale)) continue;
const links = JSON.parse(profile.links).map(l => normaliseUrl(l)).filter(l => !!l);
const linksMetadata = {};
for (let link of await db.all(SQL`SELECT * FROM links WHERE url IN (`.append(links.map(k => `'${k.replace(/'/g, "''")}'`).join(',')).append(SQL`)`))) {
@ -236,8 +239,7 @@ const fetchProfiles = async (db, username, self, opts) => {
};
}
const propv = (key, value) => opts.propv(profile, key, value);
const propv = (property, value) => opts.propv(profile.locale, property, value);
const profile_obj = {
opinions: propv("opinions", () => JSON.parse(profile.opinions)),