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

supabase / supavisor / e5e7ebfe80dbec4965226225050d4ef5c8216e88-PR-605

21 Feb 2025 02:35PM UTC coverage: 45.973% (-0.03%) from 46.003%
e5e7ebfe80dbec4965226225050d4ef5c8216e88-PR-605

Pull #605

github

hauleth
fix: remaining SSL connections that need to set `verify_none` option
Pull Request #605: fix: remaining SSL connections that need to set `verify_none` option

2 of 9 new or added lines in 3 files covered. (22.22%)

267 existing lines in 26 files now uncovered.

959 of 2086 relevant lines covered (45.97%)

635.02 hits per line

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

48.15
/lib/supavisor/tenants.ex
1
defmodule Supavisor.Tenants do
2
  @moduledoc """
3
  The Tenants context.
4
  """
5

6
  import Ecto.Query, warn: false
7
  alias Supavisor.Repo
8

9
  alias Supavisor.Tenants.Cluster
10
  alias Supavisor.Tenants.ClusterTenants
11
  alias Supavisor.Tenants.Tenant
12
  alias Supavisor.Tenants.User
13

14
  @doc """
15
  Returns the list of tenants.
16

17
  ## Examples
18

19
      iex> list_tenants()
20
      [%Tenant{}, ...]
21

22
  """
23
  def list_tenants do
24
    Repo.all(Tenant) |> Repo.preload([:users])
×
25
  end
26

27
  @doc """
28
  Gets a single tenant.
29

30
  Raises `Ecto.NoResultsError` if the Tenant does not exist.
31

32
  ## Examples
33

34
      iex> get_tenant!(123)
35
      %Tenant{}
36

37
      iex> get_tenant!(456)
38
      ** (Ecto.NoResultsError)
39

40
  """
UNCOV
41
  def get_tenant!(id), do: Repo.get!(Tenant, id)
3✔
42

43
  @spec get_tenant_by_external_id(String.t()) :: Tenant.t() | nil
44
  def get_tenant_by_external_id(external_id) do
UNCOV
45
    Tenant |> Repo.get_by(external_id: external_id) |> Repo.preload(:users)
7✔
46
  end
47

48
  @spec get_cluster_by_alias(String.t()) :: Cluster.t() | nil
49
  def get_cluster_by_alias(alias) do
50
    Cluster |> Repo.get_by(alias: alias) |> Repo.preload(:cluster_tenants)
×
51
  end
52

53
  @spec get_tenant_cache(String.t() | nil, String.t() | nil) :: Tenant.t() | nil
54
  def get_tenant_cache(external_id, sni_hostname) do
UNCOV
55
    cache_key = {:tenant_cache, external_id, sni_hostname}
2✔
56

UNCOV
57
    case Cachex.fetch(Supavisor.Cache, cache_key, fn _key ->
2✔
UNCOV
58
           {:commit, {:cached, get_tenant(external_id, sni_hostname)}, ttl: :timer.hours(24)}
2✔
59
         end) do
60
      {_, {:cached, value}} -> value
×
UNCOV
61
      {_, {:cached, value}, _} -> value
2✔
62
    end
63
  end
64

65
  @spec get_tenant(String.t() | nil, String.t() | nil) :: Tenant.t() | nil
66
  def get_tenant(nil, sni) when sni != nil do
67
    Tenant |> Repo.get_by(sni: sni)
×
68
  end
69

70
  def get_tenant(external_id, _) when external_id != nil do
UNCOV
71
    Tenant |> Repo.get_by(external_id: external_id)
2✔
72
  end
73

74
  def get_tenant(_, _), do: nil
×
75

76
  @spec get_user_cache(:single | :cluster, String.t(), String.t() | nil, String.t() | nil) ::
77
          {:ok, map()} | {:error, any()}
78
  def get_user_cache(type, user, external_id, sni_hostname) do
79
    cache_key = {:user_cache, type, user, external_id, sni_hostname}
179✔
80

81
    case Cachex.fetch(Supavisor.Cache, cache_key, fn _key ->
179✔
82
           {:commit, {:cached, get_user(type, user, external_id, sni_hostname)},
17✔
83
            ttl: :timer.hours(24)}
84
         end) do
85
      {_, {:cached, value}} -> value
162✔
86
      {_, {:cached, value}, _} -> value
17✔
87
    end
88
  end
89

90
  @spec get_user(atom(), String.t(), String.t() | nil, String.t() | nil) ::
91
          {:ok, map()} | {:error, any()}
92
  def get_user(_, _, nil, nil) do
×
93
    {:error, "Either external_id or sni_hostname must be provided"}
94
  end
95

96
  def get_user(:cluster, user, external_id, sni_hostname) do
97
    query =
×
98
      from(ct in ClusterTenants,
99
        where: ct.cluster_alias == ^external_id and ct.active == true,
100
        limit: 1
101
      )
102

103
    case Repo.all(query) do
×
104
      [%ClusterTenants{} = ct] ->
105
        get_user(:single, user, ct.tenant_external_id, sni_hostname)
×
106

107
      [_ | _] ->
×
108
        {:error, :multiple_results}
109

110
      _ ->
×
111
        {:error, :not_found}
112
    end
113
  end
114

115
  def get_user(:single, user, external_id, sni_hostname) do
116
    query = build_user_query(user, external_id, sni_hostname)
19✔
117

118
    case Repo.all(query) do
19✔
119
      [{%User{}, %Tenant{}} = {user, tenant}] ->
16✔
120
        {:ok, %{user: user, tenant: tenant}}
121

122
      [_ | _] ->
×
123
        {:error, :multiple_results}
124

UNCOV
125
      _ ->
3✔
126
        {:error, :not_found}
127
    end
128
  end
129

130
  def get_pool_config(external_id, user) do
131
    query =
16✔
132
      from(a in User,
133
        where: a.db_user_alias == ^user
134
      )
135

136
    Repo.all(
16✔
137
      from(p in Tenant,
138
        where: p.external_id == ^external_id,
139
        preload: [users: ^query]
140
      )
141
    )
142
  end
143

144
  def get_pool_config_cache(external_id, user, ttl \\ :timer.hours(24)) do
145
    ttl = if is_nil(ttl), do: :timer.hours(24), else: ttl
20✔
146
    cache_key = {:pool_config_cache, external_id, user}
20✔
147

148
    case Cachex.fetch(Supavisor.Cache, cache_key, fn _key ->
20✔
149
           {:commit, {:cached, get_pool_config(external_id, user)}, ttl: ttl}
16✔
150
         end) do
UNCOV
151
      {_, {:cached, value}} -> value
4✔
152
      {_, {:cached, value}, _} -> value
16✔
153
    end
154
  end
155

156
  @spec get_cluster_config(String.t(), String.t()) :: [ClusterTenants.t()] | {:error, any()}
157
  def get_cluster_config(external_id, user) do
158
    case Repo.all(ClusterTenants, cluster_alias: external_id) do
×
159
      [%{cluster_alias: cluster_alias, active: true} | _] ->
160
        user = from(u in User, where: u.db_user_alias == ^user)
×
161
        tenant = from(t in Tenant, preload: [users: ^user])
×
162

163
        from(ct in ClusterTenants,
164
          where: ct.cluster_alias == ^cluster_alias and ct.active == true,
165
          preload: [tenant: ^tenant]
166
        )
167
        |> Repo.all()
168
        |> Enum.reduce_while({nil, []}, &process_cluster/2)
×
169

170
      _ ->
×
171
        {:error, :not_found}
172
    end
173
  end
174

175
  defp process_cluster(cluster, {type, acc}) do
176
    type = if is_nil(type), do: cluster.tenant.require_user, else: type
×
177

178
    case cluster.tenant.users do
×
179
      [_user] when type == cluster.tenant.require_user -> {:cont, {type, [cluster | acc]}}
×
180
      [_user] -> {:halt, {:error, {:config, :different_users, cluster.tenant.external_id}}}
×
181
      _ -> {:halt, {:error, {:config, :multiple_users, cluster.tenant.external_id}}}
×
182
    end
183
  end
184

185
  @doc """
186
  Creates a tenant.
187

188
  ## Examples
189

190
      iex> create_tenant(%{field: value})
191
      {:ok, %Tenant{}}
192

193
      iex> create_tenant(%{field: bad_value})
194
      {:error, %Ecto.Changeset{}}
195

196
  """
197
  def create_tenant(attrs \\ %{}) do
198
    %Tenant{}
199
    |> Tenant.changeset(attrs)
200
    |> Repo.insert()
25✔
201
  end
202

203
  @doc """
204
  Updates a tenant.
205

206
  ## Examples
207

208
      iex> update_tenant(tenant, %{field: new_value})
209
      {:ok, %Tenant{}}
210

211
      iex> update_tenant(tenant, %{field: bad_value})
212
      {:error, %Ecto.Changeset{}}
213

214
  """
215
  def update_tenant(%Tenant{} = tenant, attrs) do
216
    tenant
217
    |> Tenant.changeset(attrs)
UNCOV
218
    |> Repo.update()
3✔
219
  end
220

221
  def update_tenant_ps(external_id, new_ps) do
222
    from(t in Tenant, where: t.external_id == ^external_id)
223
    |> Repo.one()
224
    |> Tenant.changeset(%{default_parameter_status: new_ps})
225
    |> Repo.update()
×
226
  end
227

228
  @doc """
229
  Deletes a tenant.
230

231
  ## Examples
232

233
      iex> delete_tenant(tenant)
234
      {:ok, %Tenant{}}
235

236
      iex> delete_tenant(tenant)
237
      {:error, %Ecto.Changeset{}}
238

239
  """
240
  def delete_tenant(%Tenant{} = tenant) do
UNCOV
241
    Repo.delete(tenant)
1✔
242
  end
243

244
  @spec delete_tenant_by_external_id(String.t()) :: boolean()
245
  def delete_tenant_by_external_id(id) do
246
    from(t in Tenant, where: t.external_id == ^id)
247
    |> Repo.delete_all()
UNCOV
248
    |> case do
1✔
UNCOV
249
      {num, _} when num > 0 ->
1✔
250
        true
251

252
      _ ->
×
253
        false
254
    end
255
  end
256

257
  @spec delete_cluster_by_alias(String.t()) :: boolean()
258
  def delete_cluster_by_alias(id) do
259
    from(t in Cluster, where: t.alias == ^id)
260
    |> Repo.delete_all()
261
    |> case do
×
262
      {num, _} when num > 0 ->
×
263
        true
264

265
      _ ->
×
266
        false
267
    end
268
  end
269

270
  @doc """
271
  Returns an `%Ecto.Changeset{}` for tracking tenant changes.
272

273
  ## Examples
274

275
      iex> change_tenant(tenant)
276
      %Ecto.Changeset{data: %Tenant{}}
277

278
  """
279
  def change_tenant(%Tenant{} = tenant, attrs \\ %{}) do
UNCOV
280
    Tenant.changeset(tenant, attrs)
1✔
281
  end
282

283
  alias Supavisor.Tenants.User
284

285
  @doc """
286
  Returns the list of users.
287

288
  ## Examples
289

290
      iex> list_users()
291
      [%User{}, ...]
292

293
  """
294
  def list_users do
295
    Repo.all(User)
×
296
  end
297

298
  @doc """
299
  Creates a user.
300

301
  ## Examples
302

303
      iex> create_user(%{field: value})
304
      {:ok, %User{}}
305

306
      iex> create_user(%{field: bad_value})
307
      {:error, %Ecto.Changeset{}}
308

309
  """
310
  def create_user(attrs \\ %{}) do
311
    %User{}
312
    |> User.changeset(attrs)
313
    |> Repo.insert()
×
314
  end
315

316
  @doc """
317
  Updates a user.
318

319
  ## Examples
320

321
      iex> update_user(user, %{field: new_value})
322
      {:ok, %User{}}
323

324
      iex> update_user(user, %{field: bad_value})
325
      {:error, %Ecto.Changeset{}}
326

327
  """
328
  def update_user(%User{} = user, attrs) do
329
    user
330
    |> User.changeset(attrs)
331
    |> Repo.update()
×
332
  end
333

334
  @doc """
335
  Deletes a user.
336

337
  ## Examples
338

339
      iex> delete_user(user)
340
      {:ok, %User{}}
341

342
      iex> delete_user(user)
343
      {:error, %Ecto.Changeset{}}
344

345
  """
346
  def delete_user(%User{} = user) do
347
    Repo.delete(user)
×
348
  end
349

350
  @doc """
351
  Returns an `%Ecto.Changeset{}` for tracking user changes.
352

353
  ## Examples
354

355
      iex> change_user(user)
356
      %Ecto.Changeset{data: %User{}}
357

358
  """
359
  def change_user(%User{} = user, attrs \\ %{}) do
360
    User.changeset(user, attrs)
×
361
  end
362

363
  @spec build_user_query(String.t(), String.t() | nil, String.t() | nil) ::
364
          Ecto.Queryable.t()
365
  defp build_user_query(user, external_id, sni_hostname) do
366
    from(u in User,
19✔
367
      join: t in Tenant,
368
      on: u.tenant_external_id == t.external_id,
369
      where:
370
        (u.db_user_alias == ^user and t.require_user == true) or
371
          (t.require_user == false and u.is_manager == true),
372
      select: {u, t}
373
    )
374
    |> where(^with_tenant(external_id, sni_hostname))
19✔
375
  end
376

377
  defp with_tenant(nil, sni_hostname) do
378
    dynamic([_, t], t.sni_hostname == ^sni_hostname)
×
379
  end
380

381
  defp with_tenant(external_id, _) do
382
    dynamic([_, t], t.external_id == ^external_id)
19✔
383
  end
384

385
  alias Supavisor.Tenants.Cluster
386

387
  @doc """
388
  Returns the list of clusters.
389

390
  ## Examples
391

392
      iex> list_clusters()
393
      [%Cluster{}, ...]
394

395
  """
396
  def list_clusters do
UNCOV
397
    Repo.all(Cluster)
1✔
398
  end
399

400
  @doc """
401
  Gets a single cluster.
402

403
  Raises `Ecto.NoResultsError` if the Cluster does not exist.
404

405
  ## Examples
406

407
      iex> get_cluster!(123)
408
      %Cluster{}
409

410
      iex> get_cluster!(456)
411
      ** (Ecto.NoResultsError)
412

413
  """
UNCOV
414
  def get_cluster!(id), do: Repo.get!(Cluster, id)
3✔
415

416
  @spec get_cluster_with_rel(String.t()) :: {:ok, Cluster.t()} | {:error, any()}
417
  def get_cluster_with_rel(id) do
418
    case Repo.get(Cluster, id) do
×
419
      nil ->
×
420
        {:error, :not_found}
421

422
      cluster ->
×
423
        {:ok, Repo.preload(cluster, :cluster_tenants)}
424
    end
425
  end
426

427
  @doc """
428
  Creates a cluster.
429

430
  ## Examples
431

432
      iex> create_cluster(%{field: value})
433
      {:ok, %Cluster{}}
434

435
      iex> create_cluster(%{field: bad_value})
436
      {:error, %Ecto.Changeset{}}
437

438
  """
439
  def create_cluster(attrs \\ %{}) do
440
    %Cluster{}
441
    |> Cluster.changeset(attrs)
UNCOV
442
    |> Repo.insert()
8✔
443
  end
444

445
  @doc """
446
  Updates a cluster.
447

448
  ## Examples
449

450
      iex> update_cluster(cluster, %{field: new_value})
451
      {:ok, %Cluster{}}
452

453
      iex> update_cluster(cluster, %{field: bad_value})
454
      {:error, %Ecto.Changeset{}}
455

456
  """
457
  def update_cluster(%Cluster{} = cluster, attrs) do
458
    cluster
459
    |> Cluster.changeset(attrs)
UNCOV
460
    |> Repo.update()
2✔
461
  end
462

463
  @doc """
464
  Deletes a cluster.
465

466
  ## Examples
467

468
      iex> delete_cluster(cluster)
469
      {:ok, %Cluster{}}
470

471
      iex> delete_cluster(cluster)
472
      {:error, %Ecto.Changeset{}}
473

474
  """
475
  def delete_cluster(%Cluster{} = cluster) do
UNCOV
476
    Repo.delete(cluster)
1✔
477
  end
478

479
  @doc """
480
  Returns an `%Ecto.Changeset{}` for tracking cluster changes.
481

482
  ## Examples
483

484
      iex> change_cluster(cluster)
485
      %Ecto.Changeset{data: %Cluster{}}
486

487
  """
488
  def change_cluster(%Cluster{} = cluster, attrs \\ %{}) do
UNCOV
489
    Cluster.changeset(cluster, attrs)
1✔
490
  end
491

492
  alias Supavisor.Tenants.ClusterTenants
493

494
  @doc """
495
  Returns the list of cluster_tenants.
496

497
  ## Examples
498

499
      iex> list_cluster_tenants()
500
      [%ClusterTenants{}, ...]
501

502
  """
503
  def list_cluster_tenants do
504
    Repo.all(ClusterTenants)
×
505
  end
506

507
  @doc """
508
  Gets a single cluster_tenants.
509

510
  Raises `Ecto.NoResultsError` if the Cluster tenants does not exist.
511

512
  ## Examples
513

514
      iex> get_cluster_tenants!(123)
515
      %ClusterTenants{}
516

517
      iex> get_cluster_tenants!(456)
518
      ** (Ecto.NoResultsError)
519

520
  """
521
  def get_cluster_tenants!(id), do: Repo.get!(ClusterTenants, id)
×
522

523
  @doc """
524
  Creates a cluster_tenants.
525

526
  ## Examples
527

528
      iex> create_cluster_tenants(%{field: value})
529
      {:ok, %ClusterTenants{}}
530

531
      iex> create_cluster_tenants(%{field: bad_value})
532
      {:error, %Ecto.Changeset{}}
533

534
  """
535
  def create_cluster_tenants(attrs \\ %{}) do
536
    %ClusterTenants{}
537
    |> ClusterTenants.changeset(attrs)
538
    |> Repo.insert()
×
539
  end
540

541
  @doc """
542
  Updates a cluster_tenants.
543

544
  ## Examples
545

546
      iex> update_cluster_tenants(cluster_tenants, %{field: new_value})
547
      {:ok, %ClusterTenants{}}
548

549
      iex> update_cluster_tenants(cluster_tenants, %{field: bad_value})
550
      {:error, %Ecto.Changeset{}}
551

552
  """
553
  def update_cluster_tenants(%ClusterTenants{} = cluster_tenants, attrs) do
554
    cluster_tenants
555
    |> ClusterTenants.changeset(attrs)
556
    |> Repo.update()
×
557
  end
558

559
  @doc """
560
  Deletes a cluster_tenants.
561

562
  ## Examples
563

564
      iex> delete_cluster_tenants(cluster_tenants)
565
      {:ok, %ClusterTenants{}}
566

567
      iex> delete_cluster_tenants(cluster_tenants)
568
      {:error, %Ecto.Changeset{}}
569

570
  """
571
  def delete_cluster_tenants(%ClusterTenants{} = cluster_tenants) do
572
    Repo.delete(cluster_tenants)
×
573
  end
574

575
  @doc """
576
  Returns an `%Ecto.Changeset{}` for tracking cluster_tenants changes.
577

578
  ## Examples
579

580
      iex> change_cluster_tenants(cluster_tenants)
581
      %Ecto.Changeset{data: %ClusterTenants{}}
582

583
  """
584
  def change_cluster_tenants(%ClusterTenants{} = cluster_tenants, attrs \\ %{}) do
585
    ClusterTenants.changeset(cluster_tenants, attrs)
×
586
  end
587
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