datocms-plugin-project-exporter 0.1.0

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/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "datocms-plugin-project-exporter",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "homepage": "https://github.com/mark/foobar#readme",
6
+ "description": "A plugin that allows you to export records and assets from your project directly from your browser",
7
+ "keywords": [
8
+ "datocms-plugin"
9
+ ],
10
+ "datoCmsPlugin": {
11
+ "title": "Project Exporter",
12
+ "coverImage": "docs/cover.png",
13
+ "previewImage": "docs/preview.mp4",
14
+ "entryPoint": "build/index.html",
15
+ "permissions": [
16
+ "currentUserAccessToken"
17
+ ]
18
+ },
19
+ "dependencies": {
20
+ "@datocms/cma-client-browser": "^1.2.9",
21
+ "@types/node": "^18.15.11",
22
+ "@types/react": "^18.0.32",
23
+ "@types/react-dom": "^18.0.11",
24
+ "datocms-plugin-sdk": "^0.7.4",
25
+ "datocms-react-ui": "^0.7.4",
26
+ "jszip": "^3.10.1",
27
+ "react": "^18.2.0",
28
+ "react-dom": "^18.2.0",
29
+ "react-scripts": "5.0.1",
30
+ "typescript": "^4.9.5"
31
+ },
32
+ "scripts": {
33
+ "start": "cross-env BROWSER='none' PUBLIC_URL='/' react-scripts start",
34
+ "build": "cross-env PUBLIC_URL='.' react-scripts build",
35
+ "test": "react-scripts test",
36
+ "eject": "react-scripts eject",
37
+ "prepublishOnly": "npm run build"
38
+ },
39
+ "eslintConfig": {
40
+ "extends": [
41
+ "react-app"
42
+ ]
43
+ },
44
+ "browserslist": {
45
+ "production": [
46
+ ">0.2%",
47
+ "not dead",
48
+ "not op_mini all"
49
+ ],
50
+ "development": [
51
+ "last 1 chrome version",
52
+ "last 1 firefox version",
53
+ "last 1 safari version"
54
+ ]
55
+ },
56
+ "devDependencies": {
57
+ "cross-env": "^7.0.3"
58
+ }
59
+ }
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ </head>
7
+ <body>
8
+ <noscript>You need to enable JavaScript to run this app.</noscript>
9
+ <div id="root"></div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,3 @@
1
+ # https://www.robotstxt.org/robotstxt.html
2
+ User-agent: *
3
+ Disallow:
@@ -0,0 +1,56 @@
1
+ import { RenderConfigScreenCtx } from "datocms-plugin-sdk";
2
+ import { Button, Canvas, Spinner } from "datocms-react-ui";
3
+ import s from "./styles.module.css";
4
+ import { useState } from "react";
5
+ import downloadAllRecords from "../utils/downloadAllRecords";
6
+ import downloadAllAssets from "../utils/downloadAllAssets";
7
+
8
+ type Props = {
9
+ ctx: RenderConfigScreenCtx;
10
+ };
11
+
12
+ export default function ConfigScreen({ ctx }: Props) {
13
+ const [isLoading, setLoading] = useState(false);
14
+
15
+ const handleAllRecords = async () => {
16
+ setLoading(true);
17
+ await downloadAllRecords(ctx.currentUserAccessToken as string);
18
+ setLoading(false);
19
+ };
20
+
21
+ const handleAllAssets = async () => {
22
+ setLoading(true);
23
+
24
+ await downloadAllAssets(ctx.currentUserAccessToken as string);
25
+
26
+ setLoading(false);
27
+ };
28
+
29
+ return (
30
+ <Canvas ctx={ctx}>
31
+ <div
32
+ className={isLoading ? "" : s.hidden}
33
+ style={{ height: "200px", position: "relative" }}
34
+ >
35
+ <Spinner size={48} placement="centered" />
36
+ </div>
37
+ <div className={!isLoading ? s.buttonList : s.hidden}>
38
+ You can download a specific record from its sidebar
39
+ <Button
40
+ className={s.buttonItem}
41
+ onClick={handleAllRecords}
42
+ disabled={isLoading}
43
+ >
44
+ Download all records from this project
45
+ </Button>
46
+ <Button
47
+ onClick={handleAllAssets}
48
+ className={s.buttonItem}
49
+ disabled={isLoading}
50
+ >
51
+ Download all assets from this project
52
+ </Button>
53
+ </div>
54
+ </Canvas>
55
+ );
56
+ }
@@ -0,0 +1,26 @@
1
+ import { RenderItemFormSidebarPanelCtx } from "datocms-plugin-sdk";
2
+ import { Button, Canvas } from "datocms-react-ui";
3
+
4
+ type PropTypes = {
5
+ ctx: RenderItemFormSidebarPanelCtx;
6
+ };
7
+
8
+ export default function RecordDownloaderSidebar({ ctx }: PropTypes) {
9
+ const downloadTxtFile = async () => {
10
+ const file = new Blob([JSON.stringify(ctx.formValues, null, 2)], {
11
+ type: "application/json",
12
+ });
13
+
14
+ const element = document.createElement("a");
15
+ element.href = URL.createObjectURL(file);
16
+ element.download = "datocmsRecord" + ctx.item!.id + ".json";
17
+ document.body.appendChild(element);
18
+ element.click();
19
+ };
20
+
21
+ return (
22
+ <Canvas ctx={ctx}>
23
+ <Button onClick={downloadTxtFile}>Download this record</Button>
24
+ </Canvas>
25
+ );
26
+ }
@@ -0,0 +1,10 @@
1
+ .buttonItem {
2
+ margin-top: var(--spacing-l);
3
+ }
4
+ .hidden {
5
+ display: none;
6
+ }
7
+ .buttonList {
8
+ display: flex;
9
+ flex-direction: column;
10
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,26 @@
1
+ import { RenderItemFormSidebarPanelCtx, connect } from "datocms-plugin-sdk";
2
+ import { render } from "./utils/render";
3
+ import ConfigScreen from "./entrypoints/ConfigScreen";
4
+ import "datocms-react-ui/styles.css";
5
+ import RecordDownloaderSidebar from "./entrypoints/RecordDownloaderSidebar";
6
+
7
+ connect({
8
+ renderConfigScreen(ctx) {
9
+ return render(<ConfigScreen ctx={ctx} />);
10
+ },
11
+ itemFormSidebarPanels() {
12
+ return [
13
+ {
14
+ id: "recordDownloader",
15
+ label: "Record Downloader",
16
+ startOpen: true,
17
+ },
18
+ ];
19
+ },
20
+ renderItemFormSidebarPanel(
21
+ sidebarPanelId,
22
+ ctx: RenderItemFormSidebarPanelCtx
23
+ ) {
24
+ render(<RecordDownloaderSidebar ctx={ctx} />);
25
+ },
26
+ });
@@ -0,0 +1 @@
1
+ /// <reference types="react-scripts" />
@@ -0,0 +1,27 @@
1
+ import { buildClient } from "@datocms/cma-client-browser";
2
+ import JSZip from "jszip";
3
+
4
+ export default async function downloadAllAssets(apiToken: string) {
5
+ const client = buildClient({
6
+ apiToken,
7
+ });
8
+
9
+ const zip = new JSZip();
10
+
11
+ for await (const upload of client.uploads.listPagedIterator()) {
12
+ console.log(upload);
13
+ const request = await fetch(upload.url);
14
+
15
+ const file = await request.blob();
16
+
17
+ zip.file(upload.filename, file);
18
+ }
19
+
20
+ const finishedZip = await zip.generateAsync({ type: "blob" });
21
+
22
+ const element = document.createElement("a"); //there must be a better way to do this but i didn't find it
23
+ element.href = URL.createObjectURL(finishedZip);
24
+ element.download = "allAssets.zip";
25
+ document.body.appendChild(element);
26
+ element.click();
27
+ }
@@ -0,0 +1,23 @@
1
+ import { buildClient } from "@datocms/cma-client-browser";
2
+
3
+ export default async function downloadAllRecords(apiToken: string) {
4
+ const client = buildClient({
5
+ apiToken,
6
+ });
7
+
8
+ const records = [];
9
+
10
+ for await (const record of client.items.listPagedIterator()) {
11
+ records.push(record);
12
+ }
13
+
14
+ const file = new Blob([JSON.stringify(records, null, 2)], {
15
+ type: "application/json",
16
+ });
17
+
18
+ const element = document.createElement("a"); //there must be a better way to do this but i didn't find it
19
+ element.href = URL.createObjectURL(file);
20
+ element.download = "allDatocmsRecords" + new Date().toISOString() + ".json";
21
+ document.body.appendChild(element);
22
+ element.click();
23
+ }
@@ -0,0 +1,9 @@
1
+ import React, { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+
4
+ const container = document.getElementById('root');
5
+ const root = createRoot(container!);
6
+
7
+ export function render(component: React.ReactNode): void {
8
+ root.render(<StrictMode>{component}</StrictMode>);
9
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "esModuleInterop": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "strict": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "noFallthroughCasesInSwitch": true,
12
+ "module": "esnext",
13
+ "moduleResolution": "node",
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "jsx": "react-jsx"
18
+ },
19
+ "include": ["src"]
20
+ }