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

source-academy / backend / e0330f2cf38b2d8af12bffd20f4cac2158d607fc-PR-1236

31 Mar 2025 09:12AM UTC coverage: 19.982% (-73.6%) from 93.607%
e0330f2cf38b2d8af12bffd20f4cac2158d607fc-PR-1236

Pull #1236

github

RichDom2185
Redate migrations to maintain total ordering
Pull Request #1236: Added Exam mode

12 of 57 new or added lines in 8 files covered. (21.05%)

2430 existing lines in 97 files now uncovered.

671 of 3358 relevant lines covered (19.98%)

3.03 hits per line

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

96.55
/lib/cadet/assets/assets.ex
1
defmodule Cadet.Assets.Assets do
2
  alias ExAws.{S3, S3.Upload}
3

4
  @moduledoc """
5
  Assessments context contains domain logic for assets management
6
  for Source academy's game component
7
  """
8
  @accessible_folders ~w(images locations objects avatars ui stories sfx bgm) ++
9
                        if(Mix.env() == :test, do: ["testFolder"], else: [])
10
  @accepted_file_types ~w(.jpg .jpeg .gif .png .wav .mp3 .txt)
11

12
  def upload_to_s3(upload_params, prefix, folder_name, file_name) do
13
    file_type = Path.extname(file_name)
2✔
14

15
    with :ok <- validate_file_name(file_name),
2✔
16
         :ok <- validate_folder_name(folder_name),
2✔
17
         :ok <- validate_file_type(file_type) do
2✔
18
      if object_exists?(prefix, folder_name, file_name) do
2✔
19
        {:error, {:bad_request, "File already exists"}}
20
      else
21
        file = upload_params.path
1✔
22

23
        s3_path = "#{prefix}#{folder_name}/#{file_name}"
1✔
24

25
        file
26
        |> Upload.stream_file()
27
        |> S3.upload(bucket(), s3_path)
28
        |> ExAws.request!()
1✔
29

30
        "https://#{bucket()}.s3.amazonaws.com/#{s3_path}"
1✔
31
      end
32
    end
33
  end
34

35
  def list_assets(prefix, folder_name) do
36
    prefix_len = byte_size(prefix)
2✔
37

38
    case validate_folder_name(folder_name) do
2✔
39
      :ok ->
40
        bucket()
41
        |> S3.list_objects(prefix: prefix <> folder_name <> "/")
42
        |> ExAws.stream!()
43
        |> Enum.map(fn file ->
2✔
44
          binary_part(file.key, prefix_len, byte_size(file.key) - prefix_len)
3✔
45
        end)
46

47
      {:error, _} = error ->
UNCOV
48
        error
×
49
    end
50
  end
51

52
  def delete_object(prefix, folder_name, file_name) do
53
    with :ok <- validate_file_name(file_name),
2✔
54
         :ok <- validate_folder_name(folder_name) do
2✔
55
      if object_exists?(prefix, folder_name, file_name) do
2✔
56
        bucket()
57
        |> S3.delete_object("#{prefix}#{folder_name}/#{file_name}")
1✔
58
        |> ExAws.request!()
1✔
59

60
        :ok
61
      else
62
        {:error, {:not_found, "File not found"}}
63
      end
64
    end
65
  end
66

67
  @spec object_exists?(integer() | binary(), binary, binary) :: boolean()
68
  def object_exists?(prefix, folder_name, file_name) do
69
    response =
4✔
70
      bucket()
71
      |> S3.head_object("#{prefix}#{folder_name}/#{file_name}")
4✔
72
      |> ExAws.request()
73

74
    case response do
4✔
75
      {:error, _error} -> false
2✔
76
      _ -> true
2✔
77
    end
78
  end
79

80
  defp validate_folder_name(folder_name) do
81
    if folder_name in @accessible_folders do
6✔
82
      :ok
83
    else
84
      {:error, {:bad_request, "Invalid top-level folder name"}}
85
    end
86
  end
87

88
  defp validate_file_type(file_type) do
89
    if String.downcase(file_type) in @accepted_file_types do
2✔
90
      :ok
91
    else
92
      {:error, {:bad_request, "Invalid file type"}}
93
    end
94
  end
95

96
  defp validate_file_name(file_name) do
97
    if file_name != "" do
4✔
98
      :ok
99
    else
100
      {:error, {:bad_request, "Empty file name"}}
101
    end
102
  end
103

104
  defp bucket, do: :cadet |> Application.fetch_env!(:uploader) |> Keyword.get(:assets_bucket)
9✔
105

106
  def assets_prefix,
107
    do: :cadet |> Application.fetch_env!(:uploader) |> Keyword.get(:assets_prefix, "")
6✔
108
end
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

© 2025 Coveralls, Inc