feat(portal): Enable address_description field to provide hint for accessing Resources in Clients (#5273)

Fixes #5270 

- Relaxes the `NOT NULL` constraint because in Clients we already
account for empty address descriptions (by showing the address in its
place if missing). We may want to simply hide the Resource altogether if
the description is missing (based on user feedback). With a blank field,
we can differentiate between not entered vs entered an address.
- Updates help text a bit

```[tasklist]
- [x] Update docs with examples
```

<img width="772" alt="Screenshot 2024-06-06 at 12 01 48 PM"
src="https://github.com/firezone/firezone/assets/167144/523aa0ff-f30d-44cb-bb3c-5d5cda7236e6">

---------

Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
This commit is contained in:
Jamil
2024-06-12 09:21:56 -07:00
committed by GitHub
parent 77d979e67b
commit fd3d66293c
11 changed files with 60 additions and 48 deletions

View File

@@ -5,7 +5,7 @@ defmodule Domain.Resources.Resource.Changeset do
@fields ~w[address address_description name type]a
@update_fields ~w[name address_description]a
@required_fields ~w[name address address_description type]a
@required_fields ~w[name address type]a
def create(%Accounts.Account{} = account, attrs, %Auth.Subject{} = subject) do
%Resource{connections: []}
@@ -152,8 +152,6 @@ defmodule Domain.Resources.Resource.Changeset do
changeset
|> validate_length(:name, min: 1, max: 255)
|> validate_length(:address_description, min: 1, max: 512)
# TODO: remove once address_description is visible again
|> copy_change(:address, :address_description)
|> cast_embed(:filters, with: &cast_filter/2)
|> unique_constraint(:ipv4, name: :resources_account_id_ipv4_index)
|> unique_constraint(:ipv6, name: :resources_account_id_ipv6_index)

View File

@@ -0,0 +1,9 @@
defmodule Domain.Repo.Migrations.RemoveNullConstraintFromAddressDescription do
use Ecto.Migration
def change do
alter table(:resources) do
modify(:address_description, :string, null: true)
end
end
end

View File

@@ -892,7 +892,6 @@ defmodule Domain.ResourcesTest do
assert errors_on(changeset) == %{
name: ["can't be blank"],
address: ["can't be blank"],
address_description: ["can't be blank"],
type: ["can't be blank"],
connections: ["can't be blank"]
}
@@ -987,8 +986,7 @@ defmodule Domain.ResourcesTest do
assert {:ok, resource} = create_resource(attrs, subject)
assert resource.address == attrs.address
# TODO: uncomment once we show address_description
# assert resource.address_description == attrs.address_description
assert resource.address_description == attrs.address_description
assert resource.name == attrs.address
assert resource.account_id == account.id
@@ -1021,7 +1019,7 @@ defmodule Domain.ResourcesTest do
type: :cidr,
name: "mycidr",
address: "192.168.1.1/28",
address_description: "192.168.1.1/28"
address_description: "https://google.com"
)
assert {:ok, resource} = create_resource(attrs, subject)

View File

@@ -58,16 +58,15 @@ defmodule Web.Resources.Edit do
required
/>
<div class="hidden">
<div>
<.input
field={@form[:address_description]}
type="text"
label="Address Description"
placeholder={@form[:address].value || "http://example.com/"}
required
placeholder="Enter a description or URL"
/>
<p class="mt-2 text-xs text-neutral-500">
This will be displayed in client applications to assist users in understanding how to access the resource.
Optional description or URL to show in Clients to help users access this Resource.
</p>
</div>

View File

@@ -108,16 +108,15 @@ defmodule Web.Resources.New do
</p>
</div>
<div class="hidden">
<div>
<.input
field={@form[:address_description]}
type="text"
label="Address Description"
placeholder={@form[:address].value || "http://example.com/"}
required
placeholder="Enter a description or URL"
/>
<p class="mt-2 text-xs text-neutral-500">
This will be displayed in client applications to assist users in understanding how to access the resource.
Optional description or URL to show in Clients to help users access this Resource.
</p>
</div>
@@ -161,7 +160,6 @@ defmodule Web.Resources.New do
attrs =
attrs
|> maybe_put_default_name(name_changed?)
|> maybe_put_default_address_description(address_description_changed?)
|> map_filters_form_attrs(socket.assigns.account)
|> map_connections_form_attrs()
|> maybe_put_connections(socket.assigns.params)
@@ -184,7 +182,6 @@ defmodule Web.Resources.New do
attrs =
attrs
|> maybe_put_default_name()
|> maybe_put_default_address_description()
|> map_filters_form_attrs(socket.assigns.account)
|> map_connections_form_attrs()
|> maybe_put_connections(socket.assigns.params)
@@ -212,32 +209,6 @@ defmodule Web.Resources.New do
Map.put(attrs, "name", attrs["address"])
end
defp maybe_put_default_address_description(attrs, address_description_changed? \\ true)
defp maybe_put_default_address_description(
%{"type" => "dns", "address" => address} = attrs,
false
)
when is_binary(address) do
Map.put(attrs, "address_description", "http://#{address}/")
end
defp maybe_put_default_address_description(
%{"type" => "ip", "address" => address} = attrs,
false
)
when is_binary(address) do
Map.put(attrs, "address_description", "http://#{address}/")
end
defp maybe_put_default_address_description(attrs, false) do
Map.put(attrs, "address_description", "")
end
defp maybe_put_default_address_description(attrs, true) do
attrs
end
defp maybe_put_connections(attrs, params) do
if site_id = params["site_id"] do
Map.put(attrs, "connections", %{

View File

@@ -116,7 +116,7 @@ defmodule Web.Resources.Show do
<%= @resource.address %>
</:value>
</.vertical_table_row>
<.vertical_table_row :if={false}>
<.vertical_table_row>
<:label>
Address Description
</:label>

View File

@@ -302,6 +302,7 @@ defmodule Web.Live.Resources.NewTest do
attrs = %{
name: "foobar.com",
address: "",
address_description: String.duplicate("a", 513),
filters: %{
tcp: %{ports: "80, 443", enabled: true},
udp: %{ports: "100", enabled: true}
@@ -318,7 +319,10 @@ defmodule Web.Live.Resources.NewTest do
|> form("form")
# Generate onchange to trigger visible elements, otherwise the form won't be valid
|> render_change(
resource: %{type: :dns, filters: %{tcp: %{enabled: true}, udp: %{enabled: true}}}
resource: %{
type: :dns,
filters: %{tcp: %{enabled: true}, udp: %{enabled: true}}
}
)
assert lv
@@ -326,7 +330,7 @@ defmodule Web.Live.Resources.NewTest do
|> render_submit()
|> form_validation_errors() == %{
"resource[address]" => ["can't be blank"],
"resource[address_description]" => ["can't be blank"]
"resource[address_description]" => ["should be at most 512 character(s)"]
}
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

View File

@@ -0,0 +1,6 @@
"use client";
import Content from "./readme.mdx";
export default function Page() {
return <Content />;
}

View File

@@ -1,4 +1,4 @@
import Content from "./readme.mdx";
import _Page from "./_page";
import { Metadata } from "next";
import LastUpdated from "@/components/LastUpdated";
@@ -10,7 +10,7 @@ export const metadata: Metadata = {
export default function Page() {
return (
<>
<Content />
<_Page />
<LastUpdated dirname={__dirname} />
</>
);

View File

@@ -2,6 +2,8 @@ import Alert from "@/components/DocsAlert";
import SupportOptions from "@/components/SupportOptions";
import NextStep from "@/components/NextStep";
import PlanBadge from "@/components/PlanBadge";
import Image from "next/image";
import Link from "next/link";
<PlanBadge plans={["starter", "team", "enterprise"]}>
@@ -38,6 +40,31 @@ From there, you can select the type of Resource you want to create:
before creating the Resource.
</Alert>
### Address description
When creating a Resource, you'll be given the option to add an
`address_description`. If given, this will be displayed in the Client's Resource
list to help identify the Resource. If a URL is entered, it will be displayed as
a clickable link.
<Link
target="_blank"
href="/images/kb/deploy/resources/address_description.png"
>
<Image
src="/images/kb/deploy/resources/address_description.png"
alt="Address description field"
width={600}
height={600}
className="mx-auto"
/>
</Link>
This is commonly used to show a different address to end users than the one used
for routing, where field validations are more restrictive. This can be useful to
provide a bookmark to a service like `https://gitlab.company.com`, or give hints
for accessing the service, like `10.0.0.1:2222`.
<PlanBadge plans={["team", "enterprise"]}>
### Traffic restrictions