Skip to content

Commit 2204f91

Browse files
HappyTobipatrickhulce
authored andcommitted
feat(server): add mysql support (#175)
1 parent 9cd66c2 commit 2204f91

17 files changed

+166
-29
lines changed

.travis.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,16 @@ jobs:
3939
before_script:
4040
- google-chrome-stable --version
4141
- export CHROME_PATH="$(which google-chrome-stable)"
42-
- export POSTGRES_DB_URL="postgres://postgres@localhost/lighthouse_ci_test"
4342
- export RUN_E2E_TESTS="true"
43+
- export POSTGRES_DB_URL="postgres://postgres@localhost/lighthouse_ci_test"
4444
- psql -c 'create database lighthouse_ci_test;' -U postgres
45+
- export MYSQL_DB_URL="mysql://root@localhost/lighthouse_ci_test"
46+
- mysql -e 'create database if not exists lighthouse_ci_test;' -u root
4547
script:
4648
- yarn build
4749
- yarn test
4850
- yarn ci:dogfood
4951
services:
5052
- postgresql
53+
- mysql
5154
- xvfb

docs/cli.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ Options:
288288
--logLevel [string] [choices: "silent", "verbose"] [default: "verbose"]
289289
--port, -p [number] [default: 9001]
290290
--storage.sqlDialect
291-
[string] [choices: "sqlite", "postgres"] [default: "sqlite"]
291+
[string] [choices: "sqlite", "postgres", "mysql"] [default: "sqlite"]
292292
--storage.sqlDatabasePath The path to a SQLite database on disk.
293293
--storage.sqlConnectionUrl The connection url to a postgres
294294
database.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"jest-fetch-mock": "^2.1.2",
6161
"jest-image-snapshot": "^2.9.0",
6262
"lerna": "^3.16.4",
63+
"mysql2": "^1.7.0",
6364
"parcel-bundler": "^1.12.3",
6465
"pg": "^7.12.1",
6566
"pg-hstore": "^2.3.3",

packages/cli/src/server/server.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ function buildCommand(yargs) {
2727
},
2828
'storage.sqlDialect': {
2929
type: 'string',
30-
choices: ['sqlite', 'postgres'],
30+
choices: ['sqlite', 'postgres', 'mysql'],
3131
default: 'sqlite',
3232
},
3333
'storage.sqlDatabasePath': {
3434
description: 'The path to a SQLite database on disk.',
3535
},
3636
'storage.sqlConnectionUrl': {
37-
description: 'The connection url to a postgres database.',
37+
description: 'The connection url to a postgres or mysql database.',
3838
},
3939
'storage.sqlConnectionSsl': {
4040
type: 'boolean',

packages/server/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"compression": "^1.7.4",
2323
"express": "^4.16.4",
2424
"morgan": "^1.9.1",
25-
"sequelize": "^4.42.0",
25+
"sequelize": "^4.44.3",
2626
"umzug": "^2.2.0",
2727
"uuid": "^3.3.2"
2828
},

packages/server/src/api/routes/projects.js

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ function createRouter(context) {
6969
router.get(
7070
'/:projectId/builds',
7171
handleAsyncError(async (req, res) => {
72+
if (Number.isInteger(parseInt(req.query.limit))) {
73+
req.query.limit = parseInt(req.query.limit);
74+
}
7275
const builds = await context.storageMethod.getBuilds(req.params.projectId, req.query);
7376
res.json(builds);
7477
})

packages/server/src/api/storage/sql/build-model.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ const attributes = {
2424
avatarUrl: {type: Sequelize.STRING(256)},
2525
ancestorHash: {type: Sequelize.STRING(40)},
2626
externalBuildUrl: {type: Sequelize.STRING(256)},
27-
runAt: {type: Sequelize.DATE()}, // should mostly be equal to createdAt but modifiable by the consumer
28-
committedAt: {type: Sequelize.DATE()},
29-
ancestorCommittedAt: {type: Sequelize.DATE()},
27+
runAt: {type: Sequelize.DATE(6)}, // should mostly be equal to createdAt but modifiable by the consumer
28+
committedAt: {type: Sequelize.DATE(6)},
29+
ancestorCommittedAt: {type: Sequelize.DATE(6)},
30+
createdAt: {type: Sequelize.DATE(6)},
31+
updatedAt: {type: Sequelize.DATE(6)},
3032
};
3133

3234
module.exports = {

packages/server/src/api/storage/sql/migrations/20191008-initial.js

+9-9
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ module.exports = {
1818
id: {type: Sequelize.UUID(), primaryKey: true},
1919
name: {type: Sequelize.STRING(40)},
2020
externalUrl: {type: Sequelize.STRING(256)},
21-
createdAt: {type: Sequelize.DATE()},
22-
updatedAt: {type: Sequelize.DATE()},
21+
createdAt: {type: Sequelize.DATE(6)},
22+
updatedAt: {type: Sequelize.DATE(6)},
2323
});
2424
await queryInterface.createTable('builds', {
2525
id: {type: Sequelize.UUID(), primaryKey: true},
@@ -32,9 +32,9 @@ module.exports = {
3232
avatarUrl: {type: Sequelize.STRING(256)},
3333
ancestorHash: {type: Sequelize.STRING(40)},
3434
externalBuildUrl: {type: Sequelize.STRING(256)},
35-
runAt: {type: Sequelize.DATE()},
36-
createdAt: {type: Sequelize.DATE()},
37-
updatedAt: {type: Sequelize.DATE()},
35+
runAt: {type: Sequelize.DATE(6)},
36+
createdAt: {type: Sequelize.DATE(6)},
37+
updatedAt: {type: Sequelize.DATE(6)},
3838
});
3939
await queryInterface.createTable('runs', {
4040
id: {type: Sequelize.UUID(), primaryKey: true},
@@ -43,8 +43,8 @@ module.exports = {
4343
representative: {type: Sequelize.BOOLEAN},
4444
url: {type: Sequelize.STRING({length: 256})},
4545
lhr: {type: Sequelize.TEXT},
46-
createdAt: {type: Sequelize.DATE()},
47-
updatedAt: {type: Sequelize.DATE()},
46+
createdAt: {type: Sequelize.DATE(6)},
47+
updatedAt: {type: Sequelize.DATE(6)},
4848
});
4949
await queryInterface.createTable('statistics', {
5050
id: {type: Sequelize.UUID(), primaryKey: true},
@@ -53,8 +53,8 @@ module.exports = {
5353
url: {type: Sequelize.STRING({length: 256})},
5454
name: {type: Sequelize.STRING({length: 100})},
5555
value: {type: Sequelize.NUMERIC(12, 4)},
56-
createdAt: {type: Sequelize.DATE()},
57-
updatedAt: {type: Sequelize.DATE()},
56+
createdAt: {type: Sequelize.DATE(6)},
57+
updatedAt: {type: Sequelize.DATE(6)},
5858
});
5959
},
6060
/**

packages/server/src/api/storage/sql/migrations/20191009-project-token.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ module.exports = {
1414
*/
1515
up: async (queryInterface, Sequelize) => {
1616
await queryInterface.addColumn('projects', 'token', {type: Sequelize.UUID()});
17-
await queryInterface.bulkUpdate('projects', {token: Sequelize.col('id')}, {token: null});
17+
await queryInterface.bulkUpdate(
18+
'projects',
19+
{token: Sequelize.col('id')},
20+
{token: null},
21+
{type: Sequelize.QueryTypes.BULKUPDATE}
22+
);
1823
},
1924
/**
2025
* @param {import('sequelize').QueryInterface} queryInterface

packages/server/src/api/storage/sql/migrations/20191029-committed-at.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ module.exports = {
1313
* @param {typeof import('sequelize')} Sequelize
1414
*/
1515
up: async (queryInterface, Sequelize) => {
16-
await queryInterface.addColumn('builds', 'committedAt', {type: Sequelize.DATE()});
17-
await queryInterface.addColumn('builds', 'ancestorCommittedAt', {type: Sequelize.DATE()});
16+
await queryInterface.addColumn('builds', 'committedAt', {type: Sequelize.DATE(6)});
17+
await queryInterface.addColumn('builds', 'ancestorCommittedAt', {type: Sequelize.DATE(6)});
1818
},
1919
/**
2020
* @param {import('sequelize').QueryInterface} queryInterface

packages/server/src/api/storage/sql/migrations/2019110801-project-slug.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ module.exports = {
1414
*/
1515
up: async (queryInterface, Sequelize) => {
1616
await queryInterface.addColumn('projects', 'slug', {type: Sequelize.STRING(40)});
17-
await queryInterface.bulkUpdate('projects', {slug: Sequelize.col('id')}, {slug: null});
17+
await queryInterface.bulkUpdate(
18+
'projects',
19+
{slug: Sequelize.col('id')},
20+
{slug: null},
21+
{type: Sequelize.QueryTypes.BULKUPDATE}
22+
);
1823
await queryInterface.addIndex('projects', {
1924
// @ts-ignore - Sequelize types are out of date
2025
name: 'projects_unique_slug',

packages/server/src/api/storage/sql/migrations/2019111001-statistic-version.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ module.exports = {
1414
*/
1515
up: async (queryInterface, Sequelize) => {
1616
await queryInterface.addColumn('statistics', 'version', {type: Sequelize.NUMERIC(8, 2)});
17-
await queryInterface.bulkUpdate('statistics', {version: 1}, {version: null});
17+
await queryInterface.bulkUpdate(
18+
'statistics',
19+
{version: 1},
20+
{version: null},
21+
{type: Sequelize.QueryTypes.BULKUPDATE}
22+
);
1823
},
1924
/**
2025
* @param {import('sequelize').QueryInterface} queryInterface

packages/server/src/api/storage/sql/project-model.js

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const attributes = {
1616
slug: {type: Sequelize.STRING(40)},
1717
externalUrl: {type: Sequelize.STRING(256)},
1818
token: {type: Sequelize.UUID()},
19+
createdAt: {type: Sequelize.DATE(6)},
20+
updatedAt: {type: Sequelize.DATE(6)},
1921
};
2022

2123
module.exports = {

packages/server/src/api/storage/sql/run-model.js

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const attributes = {
2020
representative: {type: Sequelize.BOOLEAN},
2121
url: {type: Sequelize.STRING({length: 256})},
2222
lhr: {type: Sequelize.TEXT('long')},
23+
createdAt: {type: Sequelize.DATE(6)},
24+
updatedAt: {type: Sequelize.DATE(6)},
2325
};
2426

2527
module.exports = {

packages/server/src/api/storage/sql/statistic-model.js

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const attributes = {
2121
url: {type: Sequelize.STRING({length: 256})},
2222
name: {type: Sequelize.STRING({length: 100})},
2323
value: {type: Sequelize.NUMERIC(12, 4)},
24+
createdAt: {type: Sequelize.DATE(6)},
25+
updatedAt: {type: Sequelize.DATE(6)},
2426
};
2527

2628
module.exports = {
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @license Copyright 2019 Google Inc. All Rights Reserved.
3+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
4+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
5+
*/
6+
'use strict';
7+
8+
/* eslint-env jest */
9+
10+
const runTests = require('./server-test-suite.js').runTests;
11+
const runServer = require('../src/server.js').createServer;
12+
13+
describe('mysql server', () => {
14+
if (!process.env.MYSQL_DB_URL) {
15+
it.skip('should work on mysql', () => {});
16+
return;
17+
}
18+
19+
const state = {
20+
port: undefined,
21+
closeServer: undefined,
22+
};
23+
24+
beforeAll(async () => {
25+
const {port, close, storageMethod} = await runServer({
26+
logLevel: 'silent',
27+
port: 0,
28+
storage: {
29+
storageMethod: 'sql',
30+
sqlDialect: 'mysql',
31+
sqlConnectionUrl: process.env.MYSQL_DB_URL,
32+
sqlConnectionSsl: !!process.env.MYSQL_DB_SSL,
33+
sqlDangerouslyResetDatabase: true,
34+
},
35+
});
36+
37+
state.port = port;
38+
state.closeServer = close;
39+
state.storageMethod = storageMethod;
40+
});
41+
42+
afterAll(() => {
43+
state.closeServer();
44+
});
45+
46+
runTests(state);
47+
});

0 commit comments

Comments
 (0)