Skip to content

Commit fb57bda

Browse files
authored
feat: Playground templates separate repository open for third party contributions (#903)
* refactor: playground templates into a new repo * packages fetching * prettier, other fixes * template packages fix * move target source * tmp path * templates define dependencies themselves * node 10 support * remove outdated test
1 parent bf828c3 commit fb57bda

File tree

96 files changed

+761
-3698
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+761
-3698
lines changed

packages/cubejs-playground/src/DashboardSource.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class DashboardSource {
1818
}
1919
}
2020

21-
async applyTemplatePackages(templatePackages, templateConfig) {
21+
async applyTemplatePackages(toApply, templateConfig = null) {
2222
if (!this.playgroundContext) {
2323
this.playgroundContext = await this.loadContext();
2424
}
@@ -28,7 +28,7 @@ class DashboardSource {
2828
'Content-Type': 'application/json'
2929
},
3030
body: JSON.stringify({
31-
templatePackages,
31+
toApply,
3232
templateConfig: templateConfig || {
3333
credentials: this.playgroundContext
3434
}
@@ -70,11 +70,12 @@ class DashboardSource {
7070
'create-react-app',
7171
Object.keys(this.installedTemplates).find(template => template.match(/-static$/)), // TODO
7272
'static-chart'
73-
], {
74-
'static-chart': {
75-
chartCode
76-
}
77-
});
73+
], { chartCode });
74+
}
75+
76+
async templates() {
77+
const { templates } = await (await fetch('/playground/manifest')).json();
78+
return templates;
7879
}
7980
}
8081

packages/cubejs-playground/src/TemplateGallery/TemplateGalleryPage.js

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { Component } from 'react';
22
import styled from 'styled-components';
33
import '@ant-design/compatible/assets/index.css';
4-
import { Card, Col, Row, Typography } from 'antd';
4+
import { Card, Col, Row, Typography, Spin } from 'antd';
55
import { Redirect, withRouter } from 'react-router-dom';
66
import DashboardSource from '../DashboardSource';
77
import { frameworks } from '../ChartContainer';
@@ -113,26 +113,35 @@ class TemplateGalleryPage extends Component {
113113
this.state = {
114114
chartLibrary: chartLibraries[0].value,
115115
framework: 'react',
116-
templatePackageName: 'react-antd-dynamic'
116+
templatePackageName: 'react-antd-dynamic',
117+
templates: null
117118
};
118119
}
119120

120121
async componentDidMount() {
121122
this.dashboardSource = new DashboardSource();
122123
await this.dashboardSource.load(true);
124+
123125
this.setState({
124-
loadError: this.dashboardSource.loadError
126+
loadError: this.dashboardSource.loadError,
127+
templates: await this.dashboardSource.templates()
125128
});
126129
}
127130

128131
render() {
129132
const {
130-
loadError
133+
loadError,
134+
templates
131135
} = this.state;
136+
132137
if (loadError && loadError.indexOf('Dashboard app not found') === -1) {
133138
return <Redirect to="/dashboard" />;
134139
}
135140

141+
if (templates == null) {
142+
return <Spin />
143+
}
144+
136145
const {
137146
chartLibrary, framework, templatePackageName, createOwnModalVisible, enableWebSocketTransport
138147
} = this.state;
@@ -142,30 +151,8 @@ class TemplateGalleryPage extends Component {
142151
const templatePackage = this.dashboardSource && this.dashboardSource.templatePackages
143152
.find(m => m.name === templatePackageName);
144153

145-
const recipes = [{
146-
name: 'Dynamic Dashboard with React, AntD, and Recharts',
147-
description: 'Use this template to create a dynamic dashboard application with React, AntD, and Chart.js. It comes with a dynamic query builder and Apollo GraphQL client. Use it when you want to allow users to edit dashboards.',
148-
coverUrl: 'https://cube.dev/downloads/template-react-dashboard.png',
149-
templatePackages: ['create-react-app', 'react-antd-dynamic', 'recharts-charts', 'antd-tables', 'credentials']
150-
}, {
151-
name: 'Real-Time Dashboard with React, AntD, and Chart.js',
152-
description: 'Use this template to create a static dashboard application with real-time WebSocket transport. Use it when users should not be allowed to edit dashboards and you want to provide them with real-time data refresh.',
153-
templatePackages: ['create-react-app', 'react-antd-static', 'chartjs-charts', 'antd-tables', 'credentials', 'web-socket-transport'],
154-
coverUrl: 'https://cube.dev/downloads/template-real-time-dashboard.png'
155-
}, {
156-
name: 'Material UI React Dashboard',
157-
coverUrl: 'https://cube.dev/downloads/template-material-ui.jpg',
158-
description: 'Use this template to create a Material UI–based static dashboard application and add charts to it by editing the source code or via Cube.js Playground. Use it when users should not be allowed to edit dashboards.',
159-
templatePackages: ['create-react-app', 'react-material-static', 'recharts-charts', 'material-tables', 'credentials']
160-
}, {
161-
name: 'Material UI D3 Dashboard',
162-
coverUrl: 'https://cube.dev/downloads/template-material-d3.png',
163-
description: 'Use this template to create a Material UI–based dashboard with D3.js. Add charts to it by editing the source code or via Cube.js Playground. Use it when users should not be allowed to edit dashboards.',
164-
templatePackages: ['create-react-app', 'react-material-static', 'd3-charts', 'material-tables', 'credentials']
165-
}];
166-
167-
168-
const recipeCards = recipes.map(({ name, description, templatePackages, coverUrl }) => (
154+
155+
const recipeCards = templates.map(({ name, description, templatePackages, coverUrl }) => (
169156
<Col xs={{ span: 24 }} md={{span: 12 }} lg={{ span: 8 }} xl={{ span: 6 }} key={name}>
170157
<RecipeCard
171158
hoverable
@@ -176,7 +163,7 @@ class TemplateGalleryPage extends Component {
176163
<Button
177164
type="primary"
178165
onClick={async () => {
179-
await this.dashboardSource.applyTemplatePackages(templatePackages);
166+
await this.dashboardSource.applyTemplatePackages(name);
180167
history.push('/dashboard');
181168
}}
182169
>
@@ -207,8 +194,8 @@ class TemplateGalleryPage extends Component {
207194
templatePackageName,
208195
`${chartLibrary}-charts`,
209196
`${templatePackageName.match(/^react-(\w+)/)[1]}-tables`, // TODO
210-
'credentials'
211-
].concat(enableWebSocketTransport ? ['web-socket-transport'] : []);
197+
'react-credentials'
198+
].concat(enableWebSocketTransport ? ['react-web-socket-transport'] : []);
212199
await this.dashboardSource.applyTemplatePackages(templatePackages);
213200
history.push('/dashboard');
214201
}}
@@ -229,10 +216,10 @@ class TemplateGalleryPage extends Component {
229216
return (
230217
<MarginFrame>
231218
<StyledTitle>
232-
Build your app from one the popular templates below or
219+
Build your app from one the popular templates below or{' '}
233220
<a onClick={() => this.setState({ createOwnModalVisible: true })}>create your own</a>
234221
</StyledTitle>
235-
<Row type="flex" align="top" gutter={24}>
222+
<Row align="top" gutter={24}>
236223
{recipeCards}
237224
</Row>
238225
</MarginFrame>

packages/cubejs-server-core/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@
2222
npm-debug.log*
2323
yarn-debug.log*
2424
yarn-error.log*
25-
test-output/
25+
test-output/
26+
/dev/__tmp__

packages/cubejs-server-core/core/DevServer.js

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
const fs = require('fs-extra');
44
const path = require('path');
55
const spawn = require('cross-spawn');
6-
const AppContainer = require('../dev/templates/AppContainer');
6+
const AppContainer = require('../dev/AppContainer');
7+
const DependencyTree = require('../dev/DependencyTree');
8+
const PackageFetcher = require('../dev/PackageFetcher');
9+
10+
const repo = {
11+
owner: 'cube-js',
12+
name: 'cubejs-playground-templates'
13+
};
714

815
class DevServer {
916
constructor(cubejsServer) {
@@ -90,8 +97,8 @@ class DevServer {
9097
let lastApplyTemplatePackagesError = null;
9198

9299
app.get('/playground/dashboard-app-create-status', catchErrors(async (req, res) => {
93-
const sourcePath = await path.join(dashboardAppPath, 'src');
94-
100+
const sourcePath = path.join(dashboardAppPath, 'src');
101+
95102
if (lastApplyTemplatePackagesError) {
96103
const toThrow = lastApplyTemplatePackagesError;
97104
lastApplyTemplatePackagesError = null;
@@ -106,9 +113,9 @@ class DevServer {
106113
await this.applyTemplatePackagesPromise;
107114
}
108115

109-
if (!(await fs.pathExists(sourcePath))) {
116+
if (!(fs.pathExistsSync(sourcePath))) {
110117
res.status(404).json({
111-
error: await fs.pathExists(dashboardAppPath) ?
118+
error: fs.pathExistsSync(dashboardAppPath) ?
112119
`Dashboard app corrupted. Please remove '${path.resolve(dashboardAppPath)}' directory and recreate it` :
113120
`Dashboard app not found in '${path.resolve(dashboardAppPath)}' directory`
114121
});
@@ -120,11 +127,9 @@ class DevServer {
120127
return;
121128
}
122129

123-
const appContainer = new AppContainer(dashboardAppPath);
124-
125130
res.json({
126131
status: 'created',
127-
installedTemplates: await appContainer.getPackageVersions()
132+
installedTemplates: AppContainer.getPackageVersions(dashboardAppPath)
128133
});
129134
}));
130135

@@ -173,24 +178,55 @@ class DevServer {
173178
}));
174179

175180
app.post('/playground/apply-template-packages', catchErrors(async (req, res) => {
181+
this.cubejsServer.event('Dev Server Download Template Packages');
182+
183+
const fetcher = new PackageFetcher(repo);
184+
176185
this.cubejsServer.event('Dev Server App File Write');
177-
const { templatePackages, templateConfig } = req.body;
178-
const appContainer = new AppContainer(dashboardAppPath, templatePackages, templateConfig);
186+
const { toApply, templateConfig } = req.body;
187+
179188
const applyTemplates = async () => {
189+
const manifestJson = await fetcher.manifestJSON();
190+
const response = await fetcher.downloadPackages();
191+
192+
let templatePackages = [];
193+
if (typeof toApply === 'string') {
194+
const template = manifestJson.templates.find(({ name }) => name === toApply);
195+
templatePackages = template.templatePackages;
196+
} else {
197+
templatePackages = toApply;
198+
}
199+
200+
const dt = new DependencyTree(manifestJson, templatePackages);
201+
202+
const appContainer = new AppContainer(
203+
dt.getRootNode(),
204+
{
205+
appPath: dashboardAppPath,
206+
packagesPath: response.packagesPath
207+
},
208+
templateConfig
209+
);
210+
180211
this.cubejsServer.event('Dev Server Create Dashboard App');
181212
await appContainer.applyTemplates();
182213
this.cubejsServer.event('Dev Server Create Dashboard App Success');
183214

184215
this.cubejsServer.event('Dev Server Dashboard Npm Install');
216+
185217
await appContainer.ensureDependencies();
186218
this.cubejsServer.event('Dev Server Dashboard Npm Install Success');
219+
220+
fetcher.cleanup();
187221
};
222+
188223
if (this.applyTemplatePackagesPromise) {
189224
this.applyTemplatePackagesPromise = this.applyTemplatePackagesPromise.then(applyTemplates);
190225
} else {
191226
this.applyTemplatePackagesPromise = applyTemplates();
192227
}
193228
const promise = this.applyTemplatePackagesPromise;
229+
194230
promise.then(() => {
195231
if (promise === this.applyTemplatePackagesPromise) {
196232
this.applyTemplatePackagesPromise = null;
@@ -203,6 +239,11 @@ class DevServer {
203239
});
204240
res.json(true); // TODO
205241
}));
242+
243+
app.get('/playground/manifest', catchErrors(async (_, res) => {
244+
const fetcher = new PackageFetcher(repo);
245+
res.json(await fetcher.manifestJSON());
246+
}));
206247

207248
app.use(serveStatic(path.join(__dirname, '../playground'), {
208249
lastModified: false,

0 commit comments

Comments
 (0)