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

EcrituresNumeriques / stylo / 15922807606

27 Jun 2025 09:18AM UTC coverage: 39.241% (+0.04%) from 39.201%
15922807606

push

github

web-flow
Crée des requêtes de prévisualisation d'articles/corpus (#1592)

Co-authored-by: Thomas Parisot <thom4parisot@users.noreply.github.com>
Co-authored-by: Guillaume Grossetie <ggrossetie@yuzutech.fr>

570 of 800 branches covered (71.25%)

Branch coverage included in aggregate %.

90 of 250 new or added lines in 17 files covered. (36.0%)

137 existing lines in 8 files now uncovered.

5638 of 15020 relevant lines covered (37.54%)

2.66 hits per line

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

63.95
/graphql/resolvers/workspaceResolver.js
1
const { ObjectId } = require('mongoose').Types
1✔
2
const { ApiError } = require('../helpers/errors')
1✔
3
const Workspace = require('../models/workspace')
1✔
4
const Article = require('../models/article')
1✔
5
const Corpus = require('../models/corpus')
1✔
6

1✔
7
async function workspace(_, { workspaceId }, { user, token }) {
×
8
  if (!workspaceId) {
×
9
    return null
×
10
  }
×
11

×
12
  if (token?.admin) {
×
13
    const workspace = await Workspace.findById(workspaceId)
×
14
    if (!workspace) {
×
NEW
15
      throw new ApiError(
×
16
        'NOT_FOUND',
×
17
        `Unable to find workspace with id ${workspaceId}`
×
18
      )
×
19
    }
×
20
    return workspace
×
21
  }
×
22

×
23
  const workspace = await Workspace.findOne({
×
24
    $and: [{ _id: workspaceId }, { 'members.user': user?._id }],
×
NEW
25
  })
×
26

×
27
  if (!workspace) {
×
28
    throw new ApiError(
×
UNCOV
29
      'NOT_FOUND',
×
UNCOV
30
      `Unable to find workspace with id ${workspaceId} for user with id ${user?._id}`
×
UNCOV
31
    )
×
UNCOV
32
  }
×
UNCOV
33
  return workspace
×
UNCOV
34
}
×
35

1✔
36
class WorkspaceArticle {
1✔
37
  constructor(workspace, article) {
1✔
38
    this.workspace = workspace
1✔
39
    this.article = article
1✔
40
  }
1✔
41

1✔
42
  async remove() {
1✔
43
    if (this.article) {
1✔
44
      this.workspace.articles.pull({ _id: this.article._id })
1✔
45
      return this.workspace.save()
1✔
46
    }
1!
UNCOV
47
    return this.workspace
×
UNCOV
48
  }
×
49
}
1✔
50

1✔
51
class WorkspaceMember {
1✔
52
  constructor(workspace, member) {
1✔
53
    this.workspace = workspace
1✔
54
    this.member = member
1✔
55
  }
1✔
56

1✔
57
  async remove() {
1✔
58
    if (this.member) {
1✔
59
      this.workspace.members.pull({ _id: this.member._id })
1✔
60
      return this.workspace.save()
1✔
61
    }
1!
UNCOV
62
    return this.workspace
×
UNCOV
63
  }
×
64
}
1✔
65

1✔
66
module.exports = {
1✔
67
  Mutation: {
1✔
68
    async createWorkspace(_, args, { user }) {
1✔
69
      const { createWorkspaceInput } = args
1✔
70
      if (!user) {
1!
UNCOV
71
        throw new ApiError(
×
UNCOV
72
          'UNAUTHENTICATED',
×
UNCOV
73
          'Unable to create a workspace as an unauthenticated user'
×
UNCOV
74
        )
×
UNCOV
75
      }
×
76
      // any user can create a workspace
1✔
77
      const newWorkspace = new Workspace({
1✔
78
        name: createWorkspaceInput.name,
1✔
79
        color: createWorkspaceInput.color,
1✔
80
        description: createWorkspaceInput.description,
1✔
81
        members: [{ user: user._id }],
1✔
82
        articles: [],
1✔
83
        creator: user._id,
1✔
84
      })
1✔
85
      return newWorkspace.save()
1✔
86
    },
1✔
87

1✔
88
    /**
1✔
89
     *
1✔
90
     */
1✔
91
    workspace,
1✔
92
  },
1✔
93

1✔
94
  Query: {
1✔
95
    /**
1✔
96
     *
1✔
97
     */
1✔
98
    workspace,
1✔
99

1✔
100
    /**
1✔
101
     *
1✔
102
     */
1✔
103
    async workspaces(_root, _args, { user }) {
1✔
104
      if (user?.admin) {
4!
UNCOV
105
        return Workspace.find()
×
106
      }
×
107
      return Workspace.find({ 'members.user': user?._id }).sort([
4✔
108
        ['updatedAt', -1],
4✔
109
      ])
4✔
110
    },
4✔
111
  },
1✔
112

1✔
113
  WorkspaceArticle: {
1✔
114
    async article(workspaceArticle, { articleId }) {
1✔
UNCOV
115
      const article = workspace.articles.find(
×
UNCOV
116
        (a) => String(a._id) === articleId
×
UNCOV
117
      )
×
UNCOV
118
      return new WorkspaceArticle(workspace, article)
×
UNCOV
119
    },
×
120
  },
1✔
121

1✔
122
  Workspace: {
1✔
123
    async article(workspace, { articleId }) {
1✔
124
      const article = workspace.articles.find(
1✔
125
        (a) => String(a._id) === articleId
1✔
126
      )
1✔
127
      return new WorkspaceArticle(workspace, article)
1✔
128
    },
1✔
129

1✔
130
    /**
1✔
131
     *
1✔
132
     * @param workspace
1✔
133
     * @param _args
1✔
134
     * @param {{ loaders: { articles } }} context
1✔
135
     * @returns {Promise<*>}
1✔
136
     */
1✔
137
    async articles(workspace, _args, context) {
1✔
138
      return Article.getArticles({
×
UNCOV
139
        filter: { _id: { $in: workspace.articles } },
×
UNCOV
140
        loaders: context.loaders,
×
UNCOV
141
      })
×
142
    },
1✔
143

1✔
144
    async corpus(workspace) {
1✔
UNCOV
145
      return Corpus.find({ workspace: workspace._id })
×
UNCOV
146
        .populate([{ path: 'creator' }])
×
UNCOV
147
        .sort([['updatedAt', -1]])
×
148
    },
1✔
149

1✔
150
    async member(workspace, { userId }) {
1✔
151
      const member = workspace.members.find((m) => String(m.user) === userId)
1✔
152
      return new WorkspaceMember(workspace, member)
1✔
153
    },
1✔
154

1✔
155
    async members(workspace, { limit }) {
1✔
156
      await workspace
1✔
157
        .populate({ path: 'members', populate: 'user', limit })
1✔
158
        .execPopulate()
1✔
159
      return workspace.members.map((m) => m.user)
1✔
160
    },
1✔
161

1✔
162
    async stats(workspace) {
1✔
UNCOV
163
      return {
×
UNCOV
164
        articlesCount: workspace.articles.length,
×
UNCOV
165
        membersCount: workspace.members.length,
×
UNCOV
166
      }
×
167
    },
1✔
168

1✔
169
    async creator(workspace, _args, context) {
1✔
170
      return await context.loaders.users.load(workspace.creator)
×
171
    },
1✔
172

1✔
173
    // mutations
1✔
174

1✔
175
    async leave(workspace, args, { user }) {
1✔
176
      if (!user) {
×
UNCOV
177
        throw new ApiError(
×
UNCOV
178
          'UNAUTHENTICATED',
×
UNCOV
179
          'Unable to leave a workspace as an unauthenticated user'
×
180
        )
×
181
      }
×
182

×
183
      // TODO: remove workspace if there's no member left!
×
184
      return Workspace.findOneAndUpdate(
×
185
        { _id: ObjectId(workspace._id) },
×
186
        { $pull: { members: { user: ObjectId(user.id) } } },
×
187
        { lean: true }
×
UNCOV
188
      )
×
189
    },
1✔
190

1✔
191
    async addArticle(workspace, { articleId }) {
1✔
UNCOV
192
      const articleAlreadyAdded = workspace.articles.find(
×
UNCOV
193
        (id) => String(id) === articleId
×
UNCOV
194
      )
×
UNCOV
195
      if (articleAlreadyAdded) {
×
196
        return workspace
×
197
      }
×
UNCOV
198
      workspace.articles.push({ _id: articleId })
×
UNCOV
199
      return workspace.save()
×
200
    },
1✔
201

1✔
202
    async inviteMember(workspace, { userId }) {
1✔
203
      // question: should we check that the authenticated user "knows" the member?
1✔
204
      const memberAlreadyInvited = workspace.members.find(
1✔
205
        (id) => String(id) === userId
1✔
206
      )
1✔
207
      if (memberAlreadyInvited) {
1!
UNCOV
208
        return workspace
×
UNCOV
209
      }
×
210
      workspace.members.push({ user: userId })
1✔
211
      return workspace.save()
1✔
212
    },
1✔
213
  },
1✔
214
}
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