• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

u-wave / core / 11085094286

28 Sep 2024 03:39PM UTC coverage: 79.715% (-0.4%) from 80.131%
11085094286

Pull #637

github

web-flow
Merge 11ccf3b06 into 14c162f19
Pull Request #637: Switch to a relational database, closes #549

751 of 918 branches covered (81.81%)

Branch coverage included in aggregate %.

1891 of 2530 new or added lines in 50 files covered. (74.74%)

13 existing lines in 7 files now uncovered.

9191 of 11554 relevant lines covered (79.55%)

68.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

24.08
/src/migrations/003-populate-sql.cjs
1
'use strict';
1✔
2

1✔
3
const { randomUUID } = require('node:crypto');
1✔
4
const { sql } = require('kysely');
1✔
5

1✔
6
/** @param {unknown} value */
1✔
NEW
7
function jsonb(value) {
×
NEW
8
  return sql`jsonb(${JSON.stringify(value)})`;
×
NEW
9
}
×
10

1✔
11
/**
1✔
12
 * @param {import('umzug').MigrationParams<import('../Uwave').default>} params
1✔
13
 */
1✔
14
async function up({ context: uw }) {
92✔
15
  const { db, models } = uw;
92✔
16

92✔
17
  /** @type {Map<string, string>} */
92✔
18
  const idMap = new Map();
92✔
19

92✔
20
  await db.transaction().execute(async (tx) => {
92✔
21
    for await (const config of models.Config.find().lean()) {
92!
NEW
22
      const { _id: name, ...value } = config;
×
NEW
23
      await tx.insertInto('configuration')
×
NEW
24
        .values({ name, value: jsonb(value) })
×
NEW
25
        .execute();
×
NEW
26
    }
×
27

92✔
28
    for await (const media of models.Media.find().lean()) {
92!
NEW
29
      const id = randomUUID();
×
NEW
30
      await tx.insertInto('media')
×
NEW
31
        .values({
×
NEW
32
          id,
×
NEW
33
          sourceType: media.sourceType,
×
NEW
34
          sourceID: media.sourceID,
×
NEW
35
          sourceData: jsonb(media.sourceData),
×
NEW
36
          artist: media.artist,
×
NEW
37
          title: media.title,
×
NEW
38
          duration: media.duration,
×
NEW
39
          thumbnail: media.thumbnail,
×
NEW
40
          createdAt: media.createdAt.toISOString(),
×
NEW
41
          updatedAt: media.updatedAt.toISOString(),
×
NEW
42
        })
×
NEW
43
        .onConflict((conflict) => conflict.columns(['sourceType', 'sourceID']).doUpdateSet({
×
NEW
44
          updatedAt: (eb) => eb.ref('excluded.updatedAt'),
×
NEW
45
        }))
×
NEW
46
        .execute();
×
NEW
47

×
NEW
48
      idMap.set(media._id.toString(), id);
×
NEW
49
    }
×
50

92✔
51
    const roles = await models.AclRole.find().lean();
92✔
52
    /** @type {Record<string, string[]>} */
92✔
53
    const roleMap = Object.create(null);
92✔
54
    for (const role of roles) {
92!
NEW
55
      if (role._id.includes('.') || role._id === '*') {
×
NEW
56
        continue;
×
NEW
57
      }
×
NEW
58

×
NEW
59
      roleMap[role._id] = role.roles ?? [];
×
NEW
60
    }
×
61
    const permissionRows = Object.entries(roleMap).map(([role, permissions]) => ({
92✔
NEW
62
      id: role,
×
NEW
63
      permissions: jsonb(
×
NEW
64
        permissions.flatMap((perm) => perm.includes('.') || perm === '*' ? [perm] : roleMap[perm]),
×
NEW
65
      ),
×
66
    }));
92✔
67

92✔
68
    if (permissionRows.length > 0) {
92!
NEW
69
      await tx.insertInto('roles')
×
NEW
70
        .values(permissionRows)
×
NEW
71
        .execute();
×
NEW
72
    }
×
73

92✔
74
    for await (const user of models.User.find().lean()) {
92!
NEW
75
      const userID = randomUUID();
×
NEW
76
      idMap.set(user._id.toString(), userID);
×
NEW
77

×
NEW
78
      await tx.insertInto('users')
×
NEW
79
        .values({
×
NEW
80
          id: userID,
×
NEW
81
          username: user.username,
×
NEW
82
          slug: user.slug,
×
NEW
83
          createdAt: user.createdAt.toISOString(),
×
NEW
84
          updatedAt: user.updatedAt.toISOString(),
×
NEW
85
        })
×
NEW
86
        .execute();
×
NEW
87

×
NEW
88
      for await (const playlist of models.Playlist.where('author', user._id).lean()) {
×
NEW
89
        const playlistID = randomUUID();
×
NEW
90
        idMap.set(playlist._id.toString(), playlistID);
×
NEW
91

×
NEW
92
        await tx.insertInto('playlists')
×
NEW
93
          .values({
×
NEW
94
            id: playlistID,
×
NEW
95
            name: playlist.name,
×
NEW
96
            userID,
×
NEW
97
            createdAt: playlist.createdAt.toISOString(),
×
NEW
98
            updatedAt: playlist.updatedAt.toISOString(),
×
NEW
99
          })
×
NEW
100
          .execute();
×
NEW
101

×
NEW
102
        const items = [];
×
NEW
103
        for (const itemMongoID of playlist.media) {
×
NEW
104
          const itemID = randomUUID();
×
NEW
105
          idMap.set(itemMongoID.toString(), itemID);
×
NEW
106

×
NEW
107
          const item = await models.PlaylistItem.findById(itemMongoID).lean();
×
NEW
108
          await tx.insertInto('playlistItems')
×
NEW
109
            .values({
×
NEW
110
              id: itemID,
×
NEW
111
              playlistID,
×
NEW
112
              mediaID: idMap.get(item.media.toString()),
×
NEW
113
              artist: item.artist,
×
NEW
114
              title: item.title,
×
NEW
115
              start: item.start,
×
NEW
116
              end: item.end,
×
NEW
117
              createdAt: item.createdAt.toISOString(),
×
NEW
118
              updatedAt: item.updatedAt.toISOString(),
×
NEW
119
            })
×
NEW
120
            .execute();
×
NEW
121

×
NEW
122
          items.push(itemID);
×
NEW
123
        }
×
NEW
124

×
NEW
125
        await tx.updateTable('playlists')
×
NEW
126
          .where('id', '=', playlistID)
×
NEW
127
          .set({ items: jsonb(items) })
×
NEW
128
          .execute();
×
NEW
129
      }
×
NEW
130
    }
×
131

92✔
132
    for await (const entry of models.Authentication.find().lean()) {
92!
NEW
133
      const userID = idMap.get(entry.user.toString());
×
NEW
134
      if (userID == null) {
×
NEW
135
        throw new Error('Migration failure: unknown user ID');
×
NEW
136
      }
×
NEW
137

×
NEW
138
      if (entry.email != null) {
×
NEW
139
        await tx.updateTable('users')
×
NEW
140
          .where('id', '=', userID)
×
NEW
141
          .set({ email: entry.email })
×
NEW
142
          .execute();
×
NEW
143
      }
×
NEW
144

×
NEW
145
      if (entry.hash != null) {
×
NEW
146
        await tx.updateTable('users')
×
NEW
147
          .where('id', '=', userID)
×
NEW
148
          .set({ password: entry.hash })
×
NEW
149
          .execute();
×
NEW
150
      }
×
NEW
151
    }
×
152

92✔
153
    for await (const entry of models.HistoryEntry.find().lean()) {
92!
NEW
154
      const entryID = randomUUID();
×
NEW
155
      idMap.set(entry._id.toString(), entryID);
×
NEW
156
      const userID = idMap.get(entry.user.toString());
×
NEW
157
      const mediaID = idMap.get(entry.media.media.toString());
×
NEW
158
      await tx.insertInto('historyEntries')
×
NEW
159
        .values({
×
NEW
160
          id: entryID,
×
NEW
161
          mediaID,
×
NEW
162
          userID,
×
NEW
163
          artist: entry.media.artist,
×
NEW
164
          title: entry.media.title,
×
NEW
165
          start: entry.media.start,
×
NEW
166
          end: entry.media.end,
×
NEW
167
          sourceData: jsonb(entry.media.sourceData),
×
NEW
168
          createdAt: entry.playedAt.toISOString(),
×
NEW
169
          // TODO vote statistics
×
NEW
170
        })
×
NEW
171
        .execute();
×
NEW
172
    }
×
173
  });
92✔
174
}
92✔
175

1✔
176
/**
1✔
177
 * @param {import('umzug').MigrationParams<import('../Uwave').default>} params
1✔
178
 */
1✔
NEW
179
async function down() {}
×
180

1✔
181
module.exports = { up, down };
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc