Passed
Push — main ( ca9167...0507b2 )
by Yume
01:10
created

controllers.CreateNewCardBulk   C

Complexity

Conditions 10

Size

Total Lines 97
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 65
nop 1
dl 0
loc 97
rs 5.3454
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like controllers.CreateNewCardBulk often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
package controllers
2
3
import (
4
	"memnixrest/app/database"
5
	"memnixrest/app/models"
6
	"memnixrest/pkg/queries"
7
	"net/http"
8
	"strconv"
9
	"strings"
10
	"sync"
11
12
	"github.com/gofiber/fiber/v2"
13
)
14
15
// GetTodayCard method
16
// @Description Get next today card
17
// @Summary get a card
18
// @Tags Card
19
// @Produce json
20
// @Success 200 {object} models.Card
21
// @Router /v1/cards/today [get]
22
func GetTodayCard(c *fiber.Ctx) error {
23
	res := *new(models.ResponseHTTP)
24
25
	auth := CheckAuth(c, models.PermUser) // Check auth
26
	if !auth.Success {
27
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
28
			Success: false,
29
			Message: auth.Message,
30
			Data:    nil,
31
			Count:   0,
32
		})
33
	}
34
35
	if res = queries.FetchNextTodayCard(c, &auth.User); !res.Success {
36
		return c.Status(http.StatusInternalServerError).JSON(models.ResponseHTTP{
37
			Success: false,
38
			Message: res.Message,
39
			Data:    nil,
40
			Count:   0,
41
		})
42
	}
43
44
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
45
		Success: true,
46
		Message: "Get todays card",
47
		Data:    res.Data,
48
		Count:   1,
49
	})
50
}
51
52
// GetNextCard method
53
// @Description Get next card
54
// @Summary get a card
55
// @Tags Card
56
// @Produce json
57
// @Success 200 {object} models.Card
58
// @Router /v1/cards/next [get]
59
func GetNextCard(c *fiber.Ctx) error {
60
	//db := database.DBConn // DB Conn
61
62
	res := *new(models.ResponseHTTP)
63
	auth := CheckAuth(c, models.PermUser) // Check auth
64
	if !auth.Success {
65
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
66
			Success: false,
67
			Message: auth.Message,
68
			Data:    nil,
69
			Count:   0,
70
		})
71
	}
72
73
	if res = queries.FetchNextCard(c, &auth.User); !res.Success {
74
		return c.Status(http.StatusInternalServerError).JSON(models.ResponseHTTP{
75
			Success: false,
76
			Message: res.Message,
77
			Data:    nil,
78
			Count:   0,
79
		})
80
	}
81
82
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
83
		Success: true,
84
		Message: "Get next card",
85
		Data:    res.Data,
86
		Count:   1,
87
	})
88
}
89
90
// GetNextCard method
91
// @Description Get next card by deckID
92
// @Summary get a card
93
// @Tags Card
94
// @Produce json
95
// @Success 200 {object} models.Card
96
// @Router /v1/cards/{deckID}/next [get]
97
func GetNextCardByDeck(c *fiber.Ctx) error {
98
	//db := database.DBConn // DB Conn
99
100
	deckID := c.Params("deckID")
101
102
	res := *new(models.ResponseHTTP)
103
	auth := CheckAuth(c, models.PermUser) // Check auth
104
	if !auth.Success {
105
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
106
			Success: false,
107
			Message: auth.Message,
108
			Data:    nil,
109
			Count:   0,
110
		})
111
	}
112
113
	if res = queries.FetchNextCardByDeck(c, &auth.User, deckID); !res.Success {
114
		return c.Status(http.StatusInternalServerError).JSON(models.ResponseHTTP{
115
			Success: false,
116
			Message: res.Message,
117
			Data:    nil,
118
			Count:   0,
119
		})
120
	}
121
122
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
123
		Success: true,
124
		Message: "Get next card by deck",
125
		Data:    res.Data,
126
		Count:   1,
127
	})
128
}
129
130
// GetAllCards method
131
// @Description Get every cards. Shouldn't really be used
132
// @Summary get all cards
133
// @Tags Card
134
// @Produce json
135
// @Success 200 {array} models.Card
136
// @Router /v1/cards/ [get]
137
func GetAllCards(c *fiber.Ctx) error {
138
	db := database.DBConn // DB Conn
139
140
	auth := CheckAuth(c, models.PermAdmin) // Check auth
141
	if !auth.Success {
142
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
143
			Success: false,
144
			Message: auth.Message,
145
			Data:    nil,
146
			Count:   0,
147
		})
148
	}
149
150
	var cards []models.Card
151
152
	if res := db.Joins("Deck").Find(&cards); res.Error != nil {
153
154
		return c.Status(http.StatusInternalServerError).JSON(models.ResponseHTTP{
155
			Success: false,
156
			Message: "Failed to get all cards",
157
			Data:    nil,
158
			Count:   0,
159
		})
160
	}
161
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
162
		Success: true,
163
		Message: "Get All cards",
164
		Data:    cards,
165
		Count:   len(cards),
166
	})
167
168
}
169
170
// GetCardByID method to get a card by id
171
// @Description Get a card by tech id
172
// @Summary get a card
173
// @Tags Card
174
// @Produce json
175
// @Param id path int true "Card ID"
176
// @Success 200 {object} models.Card
177
// @Router /v1/cards/id/{id} [get]
178
func GetCardByID(c *fiber.Ctx) error {
179
	db := database.DBConn // DB Conn
180
181
	auth := CheckAuth(c, models.PermAdmin) // Check auth
182
	if !auth.Success {
183
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
184
			Success: false,
185
			Message: auth.Message,
186
			Data:    nil,
187
			Count:   0,
188
		})
189
	}
190
191
	// Params
192
	id := c.Params("id")
193
194
	card := new(models.Card)
195
196
	if err := db.Joins("Deck").First(&card, id).Error; err != nil {
197
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
198
			Success: false,
199
			Message: err.Error(),
200
			Data:    nil,
201
			Count:   0,
202
		})
203
	}
204
205
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
206
		Success: true,
207
		Message: "Success get card by ID.",
208
		Data:    *card,
209
		Count:   1,
210
	})
211
}
212
213
// GetCardsFromDeck method to get cards from deck
214
// @Description Get every cards from a deck
215
// @Summary get a list of card
216
// @Tags Card
217
// @Produce json
218
// @Param deckID path int true "Deck ID"
219
// @Success 200 {array} models.Card
220
// @Router /v1/cards/deck/{deckID} [get]
221
func GetCardsFromDeck(c *fiber.Ctx) error {
222
	db := database.DBConn // DB Conn
223
224
	// Params
225
	id := c.Params("deckID")
226
227
	auth := CheckAuth(c, models.PermAdmin) // Check auth
228
	if !auth.Success {
229
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
230
			Success: false,
231
			Message: auth.Message,
232
			Data:    nil,
233
			Count:   0,
234
		})
235
	}
236
237
	var cards []models.Card
238
239
	if err := db.Joins("Deck").Where("cards.deck_id = ?", id).Find(&cards).Error; err != nil {
240
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
241
			Success: false,
242
			Message: err.Error(),
243
			Data:    nil,
244
			Count:   0,
245
		})
246
	}
247
248
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
249
		Success: true,
250
		Message: "Success get cards from deck.",
251
		Data:    cards,
252
		Count:   len(cards),
253
	})
254
}
255
256
// POST
257
258
// CreateNewCard method
259
// @Description Create a new card
260
// @Summary create a card
261
// @Tags Card
262
// @Produce json
263
// @Accept json
264
// @Param card body models.Card true "Card to create"
265
// @Success 200
266
// @Router /v1/cards/new [post]
267
func CreateNewCard(c *fiber.Ctx) error {
268
	db := database.DBConn // DB Conn
269
270
	card := new(models.Card)
271
272
	auth := CheckAuth(c, models.PermUser) // Check auth
273
	if !auth.Success {
274
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
275
			Success: false,
276
			Message: auth.Message,
277
			Data:    nil,
278
			Count:   0,
279
		})
280
	}
281
282
	if err := c.BodyParser(&card); err != nil {
283
		return c.Status(http.StatusBadRequest).JSON(models.ResponseHTTP{
284
			Success: false,
285
			Message: err.Error(),
286
			Data:    nil,
287
			Count:   0,
288
		})
289
	}
290
291
	if res := queries.CheckAccess(c, auth.User.ID, card.DeckID, models.AccessEditor); !res.Success {
292
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
293
			Success: false,
294
			Message: "You don't have the permission to add a card to this deck !",
295
			Data:    nil,
296
			Count:   0,
297
		})
298
	}
299
300
	if len(card.Question) < 1 || len(card.Answer) < 1 {
301
		return c.Status(http.StatusBadRequest).JSON(models.ResponseHTTP{
302
			Success: false,
303
			Message: "You must provide a question and an answer.",
304
			Data:    nil,
305
			Count:   0,
306
		})
307
	}
308
309
	db.Create(card)
310
311
	log := queries.CreateLog(models.LogCardCreated, auth.User.Username+" created "+card.Question)
312
	_ = queries.CreateUserLog(auth.User.ID, *log)
313
	_ = queries.CreateDeckLog(card.DeckID, *log)
314
	_ = queries.CreateCardLog(card.ID, *log)
315
316
	var users []models.User
317
318
	if users = queries.GetSubUsers(c, card.DeckID); len(users) > 0 {
319
320
		var wg sync.WaitGroup
321
322
		ch := make(chan models.ResponseHTTP)
323
324
		for _, s := range users {
325
			go func(c *fiber.Ctx, user models.User, card *models.Card) {
326
				res := queries.GenerateMemDate(c, &user, card)
327
				ch <- res
328
			}(c, s, card)
329
		}
330
331
		wg.Wait()
332
		close(ch)
333
	}
334
335
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
336
		Success: true,
337
		Message: "Success register a card",
338
		Data:    *card,
339
		Count:   1,
340
	})
341
}
342
343
// CreateNewCardBulk method
344
// @Description Create cards
345
// @Summary create cards
346
// @Tags Card
347
// @Produce json
348
// @Accept json
349
// @Param card body models.Card true "Cards to create"
350
// @Success 200
351
// @Router /v1/cards/deck/{deckID}/bulk [post]
352
func CreateNewCardBulk(c *fiber.Ctx) error {
353
	db := database.DBConn // DB Conn
354
355
	deckID, _ := strconv.ParseUint(c.Params("deckID"), 10, 32)
356
357
	auth := CheckAuth(c, models.PermUser) // Check auth
358
	if !auth.Success {
359
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
360
			Success: false,
361
			Message: auth.Message,
362
			Data:    nil,
363
			Count:   0,
364
		})
365
	}
366
367
	type Data struct {
368
		Cards []models.Card `json:"cards"`
369
	}
370
371
	data := new(Data)
372
373
	if err := c.BodyParser(&data); err != nil {
374
		return c.Status(http.StatusBadRequest).JSON(models.ResponseHTTP{
375
			Success: false,
376
			Message: err.Error(),
377
			Data:    nil,
378
			Count:   0,
379
		})
380
	}
381
382
	if res := queries.CheckAccess(c, auth.User.ID, uint(deckID), models.AccessEditor); !res.Success {
383
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
384
			Success: false,
385
			Message: "You don't have the permission to add a card to this deck !",
386
			Data:    nil,
387
			Count:   0,
388
		})
389
	}
390
391
	ch := make(chan models.ResponseHTTP)
392
	var wg sync.WaitGroup
393
394
	for _, card := range data.Cards {
395
		wg.Add(1)
396
		go func(c *fiber.Ctx, card models.Card, deckID uint) {
397
398
			print("Card: " + card.Question)
399
400
			var res models.ResponseHTTP
401
402
			if len(card.Question) < 1 || len(card.Answer) < 1 {
403
				res = models.ResponseHTTP{
404
					Success: false,
405
					Message: "You must provide a question and an answer.",
406
					Data:    nil,
407
					Count:   0,
408
				}
409
			} else {
410
				ca := &card
411
				ca.DeckID = deckID
412
				db.Create(ca)
413
414
				log := queries.CreateLog(models.LogCardCreated, auth.User.Username+" created "+ca.Question)
415
				_ = queries.CreateUserLog(auth.User.ID, *log)
416
				_ = queries.CreateDeckLog(deckID, *log)
417
				_ = queries.CreateCardLog(ca.ID, *log)
418
419
				res = models.ResponseHTTP{
420
					Success: true,
421
					Message: "Success creating card",
422
					Data:    nil,
423
					Count:   0,
424
				}
425
426
				var users []models.User
427
428
				if users = queries.GetSubUsers(c, deckID); len(users) > 0 {
429
430
					for _, s := range users {
431
						_ = queries.GenerateMemDate(c, &s, &card)
432
					}
433
				}
434
			}
435
			wg.Done()
436
			ch <- res
437
		}(c, card, uint(deckID))
438
439
		wg.Wait()
440
		close(ch)
441
	}
442
	//TODO: handle errors in chan
443
444
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
445
		Success: true,
446
		Message: "Success bulk creation",
447
		Data:    data.Cards,
448
		Count:   len(data.Cards),
449
	})
450
}
451
452
// PostResponse method
453
// @Description Post a response and check it
454
// @Summary post a response
455
// @Tags Card
456
// @Produce json
457
// @Success 200
458
// @Accept json
459
// @Router /v1/cards/response [post]
460
func PostResponse(c *fiber.Ctx) error {
461
	db := database.DBConn // DB Conn
462
463
	auth := CheckAuth(c, models.PermUser) // Check auth
464
	if !auth.Success {
465
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
466
			Success: false,
467
			Message: auth.Message,
468
			Data:    nil,
469
			Count:   0,
470
		})
471
	}
472
473
	response := new(models.CardResponse)
474
	card := new(models.Card)
475
476
	if err := c.BodyParser(&response); err != nil {
477
		return c.Status(http.StatusBadRequest).JSON(models.ResponseHTTP{
478
			Success: false,
479
			Message: err.Error(),
480
			Data:    nil,
481
			Count:   0,
482
		})
483
	}
484
485
	if err := db.Joins("Deck").First(&card, response.CardID).Error; err != nil {
486
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
487
			Success: false,
488
			Message: err.Error(),
489
			Data:    nil,
490
			Count:   0,
491
		})
492
	}
493
494
	res := queries.CheckAccess(c, auth.User.ID, card.Deck.ID, models.AccessStudent)
495
496
	if !res.Success {
497
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
498
			Success: false,
499
			Message: "You don't have the permission to answer this deck!",
500
			Data:    nil,
501
			Count:   0,
502
		})
503
	}
504
505
	validation := new(models.CardResponseValidation)
506
507
	if strings.EqualFold(
508
		strings.Replace(response.Response, " ", "", -1), strings.Replace(card.Answer, " ", "", -1)) {
509
		validation.Validate = true
510
		validation.Message = "Correct answer"
511
	} else {
512
		validation.Validate = false
513
		validation.Message = "Incorrect answer"
514
	}
515
516
	_ = queries.PostMem(c, auth.User, *card, *validation)
517
518
	validation.Answer = card.Answer
519
520
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
521
		Success: true,
522
		Message: "Success post response",
523
		Data:    *validation,
524
		Count:   1,
525
	})
526
}
527
528
// PUT
529
530
// UpdateCardByID method
531
// @Description Edit a card
532
// @Summary edit a card
533
// @Tags Card
534
// @Produce json
535
// @Success 200
536
// @Accept json
537
// @Param card body models.Card true "card to edit"
538
// @Router /v1/cards/{cardID}/edit [put]
539
func UpdateCardByID(c *fiber.Ctx) error {
540
	db := database.DBConn // DB Conn
541
542
	// Params
543
	id := c.Params("id")
544
545
	auth := CheckAuth(c, models.PermUser) // Check auth
546
	if !auth.Success {
547
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
548
			Success: false,
549
			Message: auth.Message,
550
			Data:    nil,
551
			Count:   0,
552
		})
553
	}
554
555
	card := new(models.Card)
556
557
	if err := db.First(&card, id).Error; err != nil {
558
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
559
			Success: false,
560
			Message: err.Error(),
561
			Data:    nil,
562
			Count:   0,
563
		})
564
	}
565
566
	if res := queries.CheckAccess(c, auth.User.ID, card.DeckID, models.AccessEditor); !res.Success {
567
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
568
			Success: false,
569
			Message: "You don't have the permission to edit this card!",
570
			Data:    nil,
571
			Count:   0,
572
		})
573
	}
574
575
	if err := UpdateCard(c, card); err != nil {
576
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
577
			Success: false,
578
			Message: "Couldn't update the card",
579
			Data:    nil,
580
			Count:   0,
581
		})
582
	}
583
584
	log := queries.CreateLog(models.LogCardEdited, auth.User.Username+" edited "+card.Question)
585
	_ = queries.CreateUserLog(auth.User.ID, *log)
586
	_ = queries.CreateCardLog(card.ID, *log)
587
588
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
589
		Success: true,
590
		Message: "Success update card by ID",
591
		Data:    *card,
592
		Count:   1,
593
	})
594
}
595
596
// UpdateDeck
597
func UpdateCard(c *fiber.Ctx, card *models.Card) error {
598
	db := database.DBConn
599
600
	if err := c.BodyParser(&card); err != nil {
601
		return c.Status(http.StatusBadRequest).JSON(models.ResponseHTTP{
602
			Success: false,
603
			Message: err.Error(),
604
			Data:    nil,
605
			Count:   0,
606
		})
607
	}
608
609
	if len(card.Question) < 1 || len(card.Answer) < 1 {
610
		return c.Status(http.StatusBadRequest).JSON(models.ResponseHTTP{
611
			Success: false,
612
			Message: "You must provide a question and an answer",
613
			Data:    nil,
614
			Count:   0,
615
		})
616
	}
617
618
	db.Save(card)
619
620
	return nil
621
}
622
623
// DeleteCardById method
624
// @Description Delete a card
625
// @Summary delete a card
626
// @Tags Card
627
// @Produce json
628
// @Success 200
629
// @Router /v1/cards/{cardID} [delete]
630
func DeleteCardById(c *fiber.Ctx) error {
631
	db := database.DBConn // DB Conn
632
	id := c.Params("id")
633
634
	auth := CheckAuth(c, models.PermUser) // Check auth
635
	if !auth.Success {
636
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
637
			Success: false,
638
			Message: auth.Message,
639
			Data:    nil,
640
			Count:   0,
641
		})
642
	}
643
644
	card := new(models.Card)
645
646
	if err := db.First(&card, id).Error; err != nil {
647
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
648
			Success: false,
649
			Message: err.Error(),
650
			Data:    nil,
651
			Count:   0,
652
		})
653
	}
654
655
	if res := queries.CheckAccess(c, auth.User.ID, card.DeckID, models.AccessOwner); !res.Success {
656
		return c.Status(http.StatusServiceUnavailable).JSON(models.ResponseHTTP{
657
			Success: false,
658
			Message: "You don't have the permission to delete this card!",
659
			Data:    nil,
660
			Count:   0,
661
		})
662
	}
663
664
	db.Delete(card)
665
666
	log := queries.CreateLog(models.LogCardDeleted, auth.User.Username+" deleted "+card.Question)
667
	_ = queries.CreateUserLog(auth.User.ID, *log)
668
	_ = queries.CreateCardLog(card.ID, *log)
669
670
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
671
		Success: true,
672
		Message: "Success delete card by ID",
673
		Data:    *card,
674
		Count:   1,
675
	})
676
677
}
678