@datawheel/bespoke 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +3115 -5095
- package/dist/server.js +100 -64
- package/package.json +7 -6
package/dist/server.js
CHANGED
|
@@ -12,7 +12,7 @@ import { strip, titleCase } from 'd3plus-text';
|
|
|
12
12
|
import axios from 'axios';
|
|
13
13
|
import slugifyFn from 'slugify';
|
|
14
14
|
import lunr from 'lunr';
|
|
15
|
-
import lunrStemmer from 'lunr-languages/lunr.stemmer.support
|
|
15
|
+
import lunrStemmer from 'lunr-languages/lunr.stemmer.support';
|
|
16
16
|
import Base58 from 'base58';
|
|
17
17
|
import FlickrSDK from 'flickr-sdk';
|
|
18
18
|
import sharp from 'sharp';
|
|
@@ -1748,19 +1748,26 @@ function ingestMembersFactory(db) {
|
|
|
1748
1748
|
attributes: [],
|
|
1749
1749
|
required: false,
|
|
1750
1750
|
right: true,
|
|
1751
|
-
where: { dimension_id, slug }
|
|
1751
|
+
where: { dimension_id, slug, id: { [Op.not]: id } }
|
|
1752
1752
|
},
|
|
1753
1753
|
group: "search.slug"
|
|
1754
1754
|
}).then((arr) => Object.fromEntries(arr.map((item) => {
|
|
1755
1755
|
const count = item.getDataValue("slugCount");
|
|
1756
1756
|
return [item.slug, Number.parseInt(`${count}`, 10)];
|
|
1757
1757
|
})));
|
|
1758
|
+
const toIngestSlugs = [];
|
|
1758
1759
|
const searchMembers = members.map((member) => {
|
|
1759
|
-
const slug2 = slugify_default(member[labelKey]);
|
|
1760
1760
|
const memberId = member[idKey];
|
|
1761
|
+
const slug2 = slugify_default(member[labelKey]);
|
|
1762
|
+
const hasDuplicateSlug = allSlugsFromOtherMembers[slug2] || toIngestSlugs.includes(slug2);
|
|
1763
|
+
if (hasDuplicateSlug) {
|
|
1764
|
+
console.log("Duplicate slug!", slug2, "replaced by", `${slug2}-${memberId}`);
|
|
1765
|
+
}
|
|
1766
|
+
const validateSlug = hasDuplicateSlug ? `${slug2}-${memberId}` : slug2;
|
|
1767
|
+
toIngestSlugs.push(slug2);
|
|
1761
1768
|
return {
|
|
1762
1769
|
id: memberId,
|
|
1763
|
-
slug:
|
|
1770
|
+
slug: validateSlug,
|
|
1764
1771
|
variant_id: id,
|
|
1765
1772
|
dimension_id,
|
|
1766
1773
|
report_id,
|
|
@@ -1780,30 +1787,39 @@ function readMemberFactory(db) {
|
|
|
1780
1787
|
const { search: Search } = db;
|
|
1781
1788
|
return readMember;
|
|
1782
1789
|
async function readMember(params) {
|
|
1783
|
-
const {
|
|
1784
|
-
mode,
|
|
1785
|
-
ids,
|
|
1786
|
-
slugs,
|
|
1787
|
-
all,
|
|
1788
|
-
locale
|
|
1789
|
-
} = params;
|
|
1790
|
+
const { all, locale, mode } = params;
|
|
1790
1791
|
const whereClause = {
|
|
1791
1792
|
content_id: []
|
|
1792
1793
|
};
|
|
1793
|
-
if (mode === "
|
|
1794
|
-
whereClause.content_id =
|
|
1794
|
+
if (mode === "content_ids") {
|
|
1795
|
+
whereClause.content_id = params.content_ids;
|
|
1796
|
+
} else if (mode === "ids") {
|
|
1797
|
+
if (all) {
|
|
1798
|
+
throw new BackendError(404, "Search by ids doesn't support locale all. Pick one locale.");
|
|
1799
|
+
}
|
|
1800
|
+
const entities = await Search.findAll({
|
|
1801
|
+
attributes: ["content_id"],
|
|
1802
|
+
where: {
|
|
1803
|
+
id: params.ids
|
|
1804
|
+
},
|
|
1805
|
+
include: [{
|
|
1806
|
+
association: "contentByLocale",
|
|
1807
|
+
where: { locale }
|
|
1808
|
+
}]
|
|
1809
|
+
});
|
|
1810
|
+
whereClause.content_id = entities.map((item) => item.content_id);
|
|
1795
1811
|
} else if (mode === "slugs") {
|
|
1796
1812
|
const entities = await Search.findAll({
|
|
1797
1813
|
attributes: ["content_id"],
|
|
1798
1814
|
where: {
|
|
1799
|
-
slug: slugs.map((item) => item.memberSlug)
|
|
1815
|
+
slug: params.slugs.map((item) => item.memberSlug)
|
|
1800
1816
|
},
|
|
1801
1817
|
include: {
|
|
1802
1818
|
association: "variant",
|
|
1803
1819
|
attributes: ["id", "name", "slug"],
|
|
1804
1820
|
required: false,
|
|
1805
1821
|
where: {
|
|
1806
|
-
slug: slugs.map((item) => item.variantSlug)
|
|
1822
|
+
slug: params.slugs.map((item) => item.variantSlug)
|
|
1807
1823
|
}
|
|
1808
1824
|
}
|
|
1809
1825
|
});
|
|
@@ -1835,48 +1851,57 @@ function readMemberFactory(db) {
|
|
|
1835
1851
|
where: contentWhereClause
|
|
1836
1852
|
}]
|
|
1837
1853
|
});
|
|
1838
|
-
if (memberSearchResults.length !==
|
|
1839
|
-
throw new BackendError(404, "One
|
|
1854
|
+
if (memberSearchResults.length !== params[mode].length) {
|
|
1855
|
+
throw new BackendError(404, "One or more members were not found.");
|
|
1840
1856
|
}
|
|
1841
1857
|
memberSearchResults.sort(
|
|
1842
1858
|
(a, b) => whereClause.content_id.indexOf(a.content_id) - whereClause.content_id.indexOf(b.content_id)
|
|
1843
1859
|
);
|
|
1844
1860
|
const normalizedResults = memberSearchResults.map((item) => {
|
|
1845
|
-
const
|
|
1846
|
-
|
|
1861
|
+
const generalResult = {
|
|
1862
|
+
id: item.id,
|
|
1863
|
+
slug: item.slug,
|
|
1864
|
+
visible: item.visible,
|
|
1865
|
+
zvalue: item.zvalue,
|
|
1866
|
+
content_id: item.content_id,
|
|
1867
|
+
report_id: item.report_id,
|
|
1847
1868
|
report_name: item.report.name,
|
|
1869
|
+
dimension_id: item.dimension_id,
|
|
1848
1870
|
dimension_name: item.dimension.name,
|
|
1871
|
+
variant_id: item.variant_id,
|
|
1849
1872
|
variant_name: item.variant.name,
|
|
1850
1873
|
variant_slug: item.variant.slug
|
|
1851
1874
|
};
|
|
1852
1875
|
if (!all) {
|
|
1853
1876
|
const localizedItem = item.getContent(locale);
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1877
|
+
const localizedImage = item.image?.getContent(locale);
|
|
1878
|
+
const image = localizedImage && {
|
|
1879
|
+
id: item.image.id,
|
|
1880
|
+
author: item.image.author,
|
|
1881
|
+
url: item.image.url,
|
|
1882
|
+
license: item.image.license,
|
|
1883
|
+
meta: localizedImage.meta
|
|
1884
|
+
};
|
|
1885
|
+
const localizedResult = {
|
|
1886
|
+
...generalResult,
|
|
1887
|
+
locale: localizedItem.locale,
|
|
1888
|
+
name: localizedItem.name,
|
|
1889
|
+
attributes: localizedItem.attributes,
|
|
1890
|
+
keywords: localizedItem.keywords,
|
|
1891
|
+
image: image || null,
|
|
1892
|
+
image_id: item.image_id
|
|
1893
|
+
};
|
|
1894
|
+
return localizedResult;
|
|
1895
|
+
} else {
|
|
1896
|
+
const contentByLocale = item.contentByLocale;
|
|
1897
|
+
generalResult.contentByLocale = contentByLocale;
|
|
1898
|
+
generalResult.image = item.image;
|
|
1899
|
+
generalResult.image_id = item.image_id;
|
|
1867
1900
|
}
|
|
1868
|
-
|
|
1869
|
-
delete newItem.dimension;
|
|
1870
|
-
delete newItem.variant;
|
|
1871
|
-
return newItem;
|
|
1901
|
+
return generalResult;
|
|
1872
1902
|
});
|
|
1873
1903
|
return {
|
|
1874
|
-
meta:
|
|
1875
|
-
locale: stripHTML(locale),
|
|
1876
|
-
mode,
|
|
1877
|
-
ids,
|
|
1878
|
-
slugs
|
|
1879
|
-
},
|
|
1904
|
+
meta: params,
|
|
1880
1905
|
results: normalizedResults
|
|
1881
1906
|
};
|
|
1882
1907
|
}
|
|
@@ -1956,16 +1981,21 @@ var verbose3 = getLogging_default();
|
|
|
1956
1981
|
var { locales } = getLocales_default();
|
|
1957
1982
|
var enabledLocales = locales.filter((d) => whitelist.includes(d));
|
|
1958
1983
|
var search;
|
|
1984
|
+
var initializing = false;
|
|
1959
1985
|
var getSearchIndexByLocale = async (db, forceRegenerate = false) => {
|
|
1960
1986
|
if (forceRegenerate) {
|
|
1961
1987
|
if (verbose3)
|
|
1962
1988
|
console.log("REGENERATING SEARCH INDEX");
|
|
1963
1989
|
search = void 0;
|
|
1990
|
+
initializing = false;
|
|
1964
1991
|
}
|
|
1965
1992
|
if (search)
|
|
1966
1993
|
return search;
|
|
1967
1994
|
try {
|
|
1968
|
-
|
|
1995
|
+
if (!initializing) {
|
|
1996
|
+
initializing = true;
|
|
1997
|
+
search = await newSearchIndex(db);
|
|
1998
|
+
}
|
|
1969
1999
|
} catch (e) {
|
|
1970
2000
|
throw new Error(`
|
|
1971
2001
|
Could not initialize Lunr search engine.
|
|
@@ -1983,7 +2013,7 @@ async function newSearchIndex(db) {
|
|
|
1983
2013
|
});
|
|
1984
2014
|
const setups = enabledLocales.map(async (locale) => {
|
|
1985
2015
|
if (!lunr[locale] && whitelist.includes(locale) && locale !== "en") {
|
|
1986
|
-
const lunrLang = await import(`lunr-languages/lunr.${locale}`);
|
|
2016
|
+
const lunrLang = (await import(`lunr-languages/lunr.${locale}`)).default;
|
|
1987
2017
|
lunrLang(lunr);
|
|
1988
2018
|
}
|
|
1989
2019
|
return [locale, lunr(function() {
|
|
@@ -2443,14 +2473,20 @@ function dbReportFactory(db) {
|
|
|
2443
2473
|
sections,
|
|
2444
2474
|
...reportData
|
|
2445
2475
|
} = data;
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2476
|
+
try {
|
|
2477
|
+
console.log("Update Report ID", id, reportData);
|
|
2478
|
+
const entity = await Report.findByPk(id, {
|
|
2479
|
+
include: reportQuery.include,
|
|
2480
|
+
rejectOnEmpty: true
|
|
2481
|
+
});
|
|
2482
|
+
entity.set(reportData);
|
|
2483
|
+
await entity.save();
|
|
2484
|
+
await entity.reload();
|
|
2485
|
+
return entity.toJSON();
|
|
2486
|
+
} catch (error) {
|
|
2487
|
+
console.log("Report id:", id, error);
|
|
2488
|
+
throw new BackendError(500, "Update Report error");
|
|
2489
|
+
}
|
|
2454
2490
|
}
|
|
2455
2491
|
async function deleteReport(id) {
|
|
2456
2492
|
const entity = await Report.findByPk(id, { rejectOnEmpty: true });
|
|
@@ -2816,15 +2852,13 @@ function endpointMemberFactory(operations) {
|
|
|
2816
2852
|
} = operations;
|
|
2817
2853
|
return [
|
|
2818
2854
|
endpoint("GET", "read/members", async (req) => {
|
|
2819
|
-
const params =
|
|
2820
|
-
|
|
2821
|
-
|
|
2855
|
+
const params = parseReadMembersParams(req.query);
|
|
2856
|
+
const list = params[params.mode] || [];
|
|
2857
|
+
if (list.length === 0) {
|
|
2858
|
+
throw new BackendError(400, `The request is missing the '${params.mode}' parameter.`);
|
|
2822
2859
|
}
|
|
2823
|
-
if (
|
|
2824
|
-
throw new BackendError(400,
|
|
2825
|
-
}
|
|
2826
|
-
if ((params.mode === "ids" ? params.ids.length : params.slugs.length) > 5) {
|
|
2827
|
-
throw new BackendError(400, "Too many 'ids' and 'slugs' in the request. Try with less than 5.");
|
|
2860
|
+
if (list.length > 20) {
|
|
2861
|
+
throw new BackendError(400, `You can look for up to 20 '${params.mode}' in a single request.`);
|
|
2828
2862
|
}
|
|
2829
2863
|
return readMember(params);
|
|
2830
2864
|
}),
|
|
@@ -2845,15 +2879,17 @@ function endpointMemberFactory(operations) {
|
|
|
2845
2879
|
})
|
|
2846
2880
|
];
|
|
2847
2881
|
}
|
|
2848
|
-
function
|
|
2849
|
-
const ids = normalizeList(query.ids)
|
|
2882
|
+
function parseReadMembersParams(query) {
|
|
2883
|
+
const ids = normalizeList(query.ids);
|
|
2884
|
+
const content_ids = normalizeList(query.content_ids).map(parseFiniteNumber);
|
|
2850
2885
|
const locale = normalizeList(query.locale)[0] || localeDefault3 || "en";
|
|
2851
2886
|
const localeIsAll = locale === "all";
|
|
2852
2887
|
return {
|
|
2853
2888
|
all: localeIsAll,
|
|
2889
|
+
locale: localeIsAll ? localeDefault3 : stripHTML(locale),
|
|
2890
|
+
mode: ids.length > 0 ? "ids" : content_ids.length > 0 ? "content_ids" : "slugs",
|
|
2854
2891
|
ids,
|
|
2855
|
-
|
|
2856
|
-
mode: ids.length > 0 ? "ids" : "slugs",
|
|
2892
|
+
content_ids,
|
|
2857
2893
|
slugs: normalizeList(query.slugs).map((token) => {
|
|
2858
2894
|
const [variantSlug, memberSlug] = token.split("/");
|
|
2859
2895
|
return { variantSlug, memberSlug };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datawheel/bespoke",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Content management system for creating automated data reports",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
"import": "./dist/server.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
-
"files": [
|
|
14
|
-
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"style": "./dist/index.css",
|
|
15
18
|
"scripts": {
|
|
16
19
|
"prepublishOnly": "tsup",
|
|
17
20
|
"build": "tsup",
|
|
@@ -81,14 +84,12 @@
|
|
|
81
84
|
"normalizr": "^3.6.2",
|
|
82
85
|
"pg": "^8.7.3",
|
|
83
86
|
"pretty-format": "^28.1.1",
|
|
84
|
-
"react": "^17.0.
|
|
87
|
+
"react": "^17.0.0||^18.0.0",
|
|
85
88
|
"react-beautiful-dnd": "^13.1.0",
|
|
86
89
|
"react-clipboard.js": "^2.0.16",
|
|
87
90
|
"react-icons": "^4.3.1",
|
|
88
91
|
"react-redux": "^7.2.6",
|
|
89
|
-
"react-router-dom": "^6.3.0",
|
|
90
92
|
"react-share": "^4.4.0",
|
|
91
|
-
"react-table-6": "^6.11.0",
|
|
92
93
|
"sequelize": "^6.17.0",
|
|
93
94
|
"sharp": "^0.31.0",
|
|
94
95
|
"shelljs": "^0.8.5",
|