Update e2e tests and n8n node for the portal

Follow the new domain model in tests: drop organization profile
assertions, add a trust center profile test, and hit the dedicated HTTPS
listener with SNI for the visitor API. Mirror the custom link rename and
profile field moves in the n8n node operations.

Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
Bryan Frimin
2026-07-10 15:15:24 +02:00
parent ce1b64b529
commit ebe6192a0c
12 changed files with 435 additions and 232 deletions

View File

@@ -34,7 +34,7 @@ func TestOrganization_Update(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
t.Run("update name and description", func(t *testing.T) {
t.Run("update name", func(t *testing.T) {
newName := fmt.Sprintf("Updated Org %d", time.Now().UnixNano())
query := `
@@ -43,7 +43,6 @@ func TestOrganization_Update(t *testing.T) {
organization {
id
name
description
}
}
}
@@ -52,9 +51,8 @@ func TestOrganization_Update(t *testing.T) {
var result struct {
UpdateOrganization struct {
Organization struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
ID string `json:"id"`
Name string `json:"name"`
} `json:"organization"`
} `json:"updateOrganization"`
}
@@ -63,82 +61,12 @@ func TestOrganization_Update(t *testing.T) {
"input": map[string]any{
"organizationId": owner.GetOrganizationID().String(),
"name": newName,
"description": "Updated organization description",
},
}, &result)
require.NoError(t, err)
assert.Equal(t, owner.GetOrganizationID().String(), result.UpdateOrganization.Organization.ID)
assert.Equal(t, newName, result.UpdateOrganization.Organization.Name)
assert.Equal(t, "Updated organization description", result.UpdateOrganization.Organization.Description)
})
t.Run("update website and email", func(t *testing.T) {
query := `
mutation UpdateOrganization($input: UpdateOrganizationInput!) {
updateOrganization(input: $input) {
organization {
id
websiteUrl
email
}
}
}
`
var result struct {
UpdateOrganization struct {
Organization struct {
ID string `json:"id"`
WebsiteUrl string `json:"websiteUrl"`
Email string `json:"email"`
} `json:"organization"`
} `json:"updateOrganization"`
}
err := owner.ExecuteConnect(query, map[string]any{
"input": map[string]any{
"organizationId": owner.GetOrganizationID().String(),
"websiteUrl": "https://example.com",
"email": "contact@example.com",
},
}, &result)
require.NoError(t, err)
assert.Equal(t, "https://example.com", result.UpdateOrganization.Organization.WebsiteUrl)
assert.Equal(t, "contact@example.com", result.UpdateOrganization.Organization.Email)
})
t.Run("update headquarter address", func(t *testing.T) {
query := `
mutation UpdateOrganization($input: UpdateOrganizationInput!) {
updateOrganization(input: $input) {
organization {
id
headquarterAddress
}
}
}
`
var result struct {
UpdateOrganization struct {
Organization struct {
ID string `json:"id"`
HeadquarterAddress string `json:"headquarterAddress"`
} `json:"organization"`
} `json:"updateOrganization"`
}
err := owner.ExecuteConnect(query, map[string]any{
"input": map[string]any{
"organizationId": owner.GetOrganizationID().String(),
"headquarterAddress": "123 Main St, Suite 100, San Francisco, CA 94102",
},
}, &result)
require.NoError(t, err)
assert.Equal(t, "123 Main St, Suite 100, San Francisco, CA 94102", result.UpdateOrganization.Organization.HeadquarterAddress)
})
}
@@ -200,10 +128,6 @@ func TestOrganization_Get(t *testing.T) {
... on Organization {
id
name
description
websiteUrl
email
headquarterAddress
}
}
}
@@ -211,12 +135,8 @@ func TestOrganization_Get(t *testing.T) {
var result struct {
Node struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
WebsiteUrl string `json:"websiteUrl"`
Email string `json:"email"`
HeadquarterAddress string `json:"headquarterAddress"`
ID string `json:"id"`
Name string `json:"name"`
} `json:"node"`
}

View File

@@ -1324,7 +1324,7 @@ func TestRBAC(t *testing.T) {
client: owner,
query: updateOrganizationMutation,
variables: func() map[string]any {
return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "description": factory.SafeName("Updated Desc")}}
return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "name": factory.SafeName("Updated Org")}}
},
shouldAllow: true,
useConnect: true,
@@ -1335,7 +1335,7 @@ func TestRBAC(t *testing.T) {
client: admin,
query: updateOrganizationMutation,
variables: func() map[string]any {
return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "description": factory.SafeName("Updated Desc")}}
return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "name": factory.SafeName("Updated Org")}}
},
shouldAllow: true,
useConnect: true,
@@ -1346,7 +1346,7 @@ func TestRBAC(t *testing.T) {
client: viewer,
query: updateOrganizationMutation,
variables: func() map[string]any {
return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "description": factory.SafeName("Updated Desc")}}
return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "name": factory.SafeName("Updated Org")}}
},
shouldAllow: false,
useConnect: true,

View File

@@ -0,0 +1,106 @@
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package console_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.probo.inc/probo/e2e/internal/testutil"
)
func TestTrustCenter_UpdateProfile(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
organizationID := owner.GetOrganizationID().String()
const trustCenterQuery = `
query($organizationId: ID!) {
node(id: $organizationId) {
... on Organization {
trustCenter {
id
}
}
}
}
`
var trustCenterLookup struct {
Node struct {
TrustCenter struct {
ID string `json:"id"`
} `json:"trustCenter"`
} `json:"node"`
}
err := owner.Execute(trustCenterQuery, map[string]any{
"organizationId": organizationID,
}, &trustCenterLookup)
require.NoError(t, err)
require.NotEmpty(t, trustCenterLookup.Node.TrustCenter.ID)
trustCenterID := trustCenterLookup.Node.TrustCenter.ID
const updateMutation = `
mutation UpdateTrustCenter($input: UpdateTrustCenterInput!) {
updateTrustCenter(input: $input) {
trustCenter {
id
description
websiteUrl
email
headquarterAddress
}
}
}
`
var result struct {
UpdateTrustCenter struct {
TrustCenter struct {
ID string `json:"id"`
Description *string `json:"description"`
WebsiteURL *string `json:"websiteUrl"`
Email *string `json:"email"`
HeadquarterAddress *string `json:"headquarterAddress"`
} `json:"trustCenter"`
} `json:"updateTrustCenter"`
}
err = owner.Execute(updateMutation, map[string]any{
"input": map[string]any{
"trustCenterId": trustCenterID,
"description": "We keep your data safe.",
"websiteUrl": "https://example.com",
"email": "security@example.com",
"headquarterAddress": "123 Main St, San Francisco, CA 94102",
},
}, &result)
require.NoError(t, err)
tc := result.UpdateTrustCenter.TrustCenter
assert.Equal(t, trustCenterID, tc.ID)
require.NotNil(t, tc.Description)
assert.Equal(t, "We keep your data safe.", *tc.Description)
require.NotNil(t, tc.WebsiteURL)
assert.Equal(t, "https://example.com", *tc.WebsiteURL)
require.NotNil(t, tc.Email)
assert.Equal(t, "security@example.com", *tc.Email)
require.NotNil(t, tc.HeadquarterAddress)
assert.Equal(t, "123 Main St, San Francisco, CA 94102", *tc.HeadquarterAddress)
}

View File

@@ -22,10 +22,13 @@ package testutil
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net"
"net/http"
"net/textproto"
"testing"
@@ -34,6 +37,11 @@ import (
"github.com/stretchr/testify/require"
)
// trustCenterHTTPSAddr is the loopback address of the dedicated trust-center
// HTTPS listener started by the e2e probod (see generateConfig). Compliance
// pages are served here exclusively, routed by TLS SNI / Host header.
const trustCenterHTTPSAddr = "127.0.0.1:10443"
type GraphQLRequest struct {
Query string `json:"query"`
Variables map[string]any `json:"variables,omitempty"`
@@ -206,8 +214,76 @@ func ConsoleGraphQLWithAccessToken(
return &gqlResp, nil
}
func (c *Client) DoTrust(trustCenterID string, query string, variables map[string]any) (*GraphQLResponse, error) {
return c.doWithEndpoint(fmt.Sprintf("/trust/%s/api/trust/v1/graphql", trustCenterID), query, variables)
// trustHTTPClient builds an HTTP client that always dials the dedicated
// trust-center HTTPS listener on loopback while presenting the compliance
// page's host as TLS SNI. Certificates are Pebble-issued for e2e, so
// verification is skipped.
func trustHTTPClient(serverName string) *http.Client {
dialer := &net.Dialer{Timeout: 5 * time.Second}
return &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
return dialer.DialContext(ctx, "tcp", trustCenterHTTPSAddr)
},
TLSClientConfig: &tls.Config{
ServerName: serverName,
InsecureSkipVerify: true, //nolint:gosec // e2e talks to Pebble-issued certs on loopback.
},
},
}
}
// DoTrust posts a GraphQL query to a compliance page served on the dedicated
// listener. host is the page's serving domain (a customer custom domain or a
// managed {slug}.probopage.localhost subdomain).
func (c *Client) DoTrust(host string, query string, variables map[string]any) (*GraphQLResponse, error) {
reqBody := GraphQLRequest{
Query: query,
Variables: variables,
}
body, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("cannot marshal request: %w", err)
}
endpoint := fmt.Sprintf("https://%s/api/trust/v1/graphql", host)
req, err := http.NewRequest("POST", endpoint, bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("cannot create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
resp, err := trustHTTPClient(host).Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer func() { _ = resp.Body.Close() }()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("cannot read response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(respBody))
}
var gqlResp GraphQLResponse
if err := json.Unmarshal(respBody, &gqlResp); err != nil {
return nil, fmt.Errorf("cannot decode response: %w", err)
}
if len(gqlResp.Errors) > 0 {
return &gqlResp, GraphQLErrors(gqlResp.Errors)
}
return &gqlResp, nil
}
func (c *Client) Execute(query string, variables map[string]any, result any) error {
@@ -240,8 +316,8 @@ func (c *Client) ExecuteConnect(query string, variables map[string]any, result a
return nil
}
func (c *Client) ExecuteTrust(trustCenterID string, query string, variables map[string]any, result any) error {
resp, err := c.DoTrust(trustCenterID, query, variables)
func (c *Client) ExecuteTrust(host string, query string, variables map[string]any, result any) error {
resp, err := c.DoTrust(host, query, variables)
if err != nil {
return err
}

View File

@@ -293,9 +293,17 @@ func generateConfig() (string, error) {
"PROBOD_OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION": "5",
"PROBOD_OAUTH2_SERVER_DEVICE_CODE_DURATION": "15",
// Trust center.
"PROBOD_TRUST_CENTER_HTTP_ADDR": ":10080",
"PROBOD_TRUST_CENTER_HTTPS_ADDR": ":10443",
// Trust center. Compliance pages are served exclusively over this
// dedicated listener, addressed by Host/SNI. The managed base domain
// yields {slug}.probopage.localhost subdomains for pages without a
// customer custom domain.
"PROBOD_TRUST_CENTER_HTTP_ADDR": ":10080",
"PROBOD_TRUST_CENTER_HTTPS_ADDR": ":10443",
"PROBOD_TRUST_CENTER_BASE_DOMAIN": "probopage.localhost",
// Keep certificate provisioning snappy so trust-center e2e flows do not
// wait on the default 30s poll (Pebble runs with PEBBLE_VA_ALWAYS_VALID).
"PROBOD_CUSTOM_DOMAINS_PROVISION_INTERVAL": "1",
// AWS / S3 (SeaweedFS).
"PROBOD_AWS_BUCKET": "probod-test",

View File

@@ -49,7 +49,7 @@ type trustCenterReference struct {
Order int `json:"order"`
}
type complianceExternalURL struct {
type complianceCustomLink struct {
ID string `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
@@ -174,17 +174,31 @@ func TestMCP_UpdateTrustCenter(t *testing.T) {
// Update
var updateResult struct {
TrustCenter trustCenter `json:"trustCenter"`
TrustCenter struct {
ID string `json:"id"`
Description *string `json:"description"`
WebsiteURL *string `json:"website_url"`
Email *string `json:"email"`
HeadquarterAddress *string `json:"headquarter_address"`
} `json:"trustCenter"`
}
mc.CallToolInto("updateTrustCenter", map[string]any{
"id": getResult.TrustCenter.ID,
"companyName": "Updated Company",
"pageTitle": "Updated Trust Center",
"trust_center_id": getResult.TrustCenter.ID,
"description": "We keep your data safe.",
"website_url": "https://example.com",
"email": "security@example.com",
"headquarter_address": "123 Main St, San Francisco, CA 94102",
}, &updateResult)
assert.Equal(t, getResult.TrustCenter.ID, updateResult.TrustCenter.ID)
assert.Equal(t, "Updated Company", updateResult.TrustCenter.CompanyName)
assert.Equal(t, "Updated Trust Center", updateResult.TrustCenter.PageTitle)
require.NotNil(t, updateResult.TrustCenter.Description)
assert.Equal(t, "We keep your data safe.", *updateResult.TrustCenter.Description)
require.NotNil(t, updateResult.TrustCenter.WebsiteURL)
assert.Equal(t, "https://example.com", *updateResult.TrustCenter.WebsiteURL)
require.NotNil(t, updateResult.TrustCenter.Email)
assert.Equal(t, "security@example.com", *updateResult.TrustCenter.Email)
require.NotNil(t, updateResult.TrustCenter.HeadquarterAddress)
assert.Equal(t, "123 Main St, San Francisco, CA 94102", *updateResult.TrustCenter.HeadquarterAddress)
}
func TestMCP_AddTrustCenterReference(t *testing.T) {
@@ -363,7 +377,7 @@ func TestMCP_ListTrustCenterFiles(t *testing.T) {
assert.NotNil(t, listResult.TrustCenterFiles)
}
func TestMCP_AddComplianceExternalURL(t *testing.T) {
func TestMCP_AddComplianceCustomLink(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
mc := testutil.NewMCPClient(t, owner)
@@ -379,19 +393,19 @@ func TestMCP_AddComplianceExternalURL(t *testing.T) {
tcID := getResult.TrustCenter.ID
var result struct {
ComplianceExternalURL complianceExternalURL `json:"complianceExternalUrl"`
ComplianceCustomLink complianceCustomLink `json:"complianceCustomLink"`
}
mc.CallToolInto("addComplianceExternalURL", map[string]any{
mc.CallToolInto("addComplianceCustomLink", map[string]any{
"trustCenterId": tcID,
"name": "ISO 27001 Certificate",
"url": "https://example.com/iso27001",
}, &result)
assert.NotEmpty(t, result.ComplianceExternalURL.ID)
assert.Equal(t, "ISO 27001 Certificate", result.ComplianceExternalURL.Name)
assert.NotEmpty(t, result.ComplianceCustomLink.ID)
assert.Equal(t, "ISO 27001 Certificate", result.ComplianceCustomLink.Name)
}
func TestMCP_UpdateComplianceExternalURL(t *testing.T) {
func TestMCP_UpdateComplianceCustomLink(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
mc := testutil.NewMCPClient(t, owner)
@@ -408,30 +422,30 @@ func TestMCP_UpdateComplianceExternalURL(t *testing.T) {
// Create
var addResult struct {
ComplianceExternalURL complianceExternalURL `json:"complianceExternalUrl"`
ComplianceCustomLink complianceCustomLink `json:"complianceCustomLink"`
}
mc.CallToolInto("addComplianceExternalURL", map[string]any{
mc.CallToolInto("addComplianceCustomLink", map[string]any{
"trustCenterId": tcID,
"name": "Original URL",
"url": "https://example.com/original",
}, &addResult)
require.NotEmpty(t, addResult.ComplianceExternalURL.ID)
require.NotEmpty(t, addResult.ComplianceCustomLink.ID)
// Update
var updateResult struct {
ComplianceExternalURL complianceExternalURL `json:"complianceExternalUrl"`
ComplianceCustomLink complianceCustomLink `json:"complianceCustomLink"`
}
mc.CallToolInto("updateComplianceExternalURL", map[string]any{
"id": addResult.ComplianceExternalURL.ID,
mc.CallToolInto("updateComplianceCustomLink", map[string]any{
"id": addResult.ComplianceCustomLink.ID,
"name": "Updated URL",
"url": "https://example.com/updated",
}, &updateResult)
assert.Equal(t, addResult.ComplianceExternalURL.ID, updateResult.ComplianceExternalURL.ID)
assert.Equal(t, "Updated URL", updateResult.ComplianceExternalURL.Name)
assert.Equal(t, addResult.ComplianceCustomLink.ID, updateResult.ComplianceCustomLink.ID)
assert.Equal(t, "Updated URL", updateResult.ComplianceCustomLink.Name)
}
func TestMCP_DeleteComplianceExternalURL(t *testing.T) {
func TestMCP_DeleteComplianceCustomLink(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
mc := testutil.NewMCPClient(t, owner)
@@ -448,27 +462,27 @@ func TestMCP_DeleteComplianceExternalURL(t *testing.T) {
// Create
var addResult struct {
ComplianceExternalURL complianceExternalURL `json:"complianceExternalUrl"`
ComplianceCustomLink complianceCustomLink `json:"complianceCustomLink"`
}
mc.CallToolInto("addComplianceExternalURL", map[string]any{
mc.CallToolInto("addComplianceCustomLink", map[string]any{
"trustCenterId": tcID,
"name": "URL to delete",
"url": "https://example.com/delete",
}, &addResult)
require.NotEmpty(t, addResult.ComplianceExternalURL.ID)
require.NotEmpty(t, addResult.ComplianceCustomLink.ID)
// Delete
var deleteResult struct {
DeletedComplianceExternalURLID string `json:"deletedComplianceExternalUrlId"`
DeletedComplianceCustomLinkID string `json:"deletedComplianceCustomLinkId"`
}
mc.CallToolInto("deleteComplianceExternalURL", map[string]any{
"id": addResult.ComplianceExternalURL.ID,
mc.CallToolInto("deleteComplianceCustomLink", map[string]any{
"id": addResult.ComplianceCustomLink.ID,
}, &deleteResult)
assert.Equal(t, addResult.ComplianceExternalURL.ID, deleteResult.DeletedComplianceExternalURLID)
assert.Equal(t, addResult.ComplianceCustomLink.ID, deleteResult.DeletedComplianceCustomLinkID)
}
func TestMCP_ListComplianceExternalURLs(t *testing.T) {
func TestMCP_ListComplianceCustomLinks(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
mc := testutil.NewMCPClient(t, owner)
@@ -486,25 +500,25 @@ func TestMCP_ListComplianceExternalURLs(t *testing.T) {
// Create URLs
for i := range 2 {
var result struct {
ComplianceExternalURL complianceExternalURL `json:"complianceExternalUrl"`
ComplianceCustomLink complianceCustomLink `json:"complianceCustomLink"`
}
mc.CallToolInto("addComplianceExternalURL", map[string]any{
mc.CallToolInto("addComplianceCustomLink", map[string]any{
"trustCenterId": tcID,
"name": factory.SafeName("URL"),
"url": "https://example.com/" + factory.SafeName("path"),
}, &result)
require.NotEmpty(t, result.ComplianceExternalURL.ID)
require.NotEmpty(t, result.ComplianceCustomLink.ID)
_ = i
}
// List
var listResult struct {
ComplianceExternalURLs []complianceExternalURL `json:"complianceExternalUrls"`
ComplianceCustomLinks []complianceCustomLink `json:"complianceCustomLinks"`
}
mc.CallToolInto("listComplianceExternalURLs", map[string]any{
mc.CallToolInto("listComplianceCustomLinks", map[string]any{
"trustCenterId": tcID,
}, &listResult)
assert.GreaterOrEqual(t, len(listResult.ComplianceExternalURLs), 2)
assert.GreaterOrEqual(t, len(listResult.ComplianceCustomLinks), 2)
}

View File

@@ -23,6 +23,7 @@ package trust_test
import (
"io"
"net/http"
"net/url"
"strings"
"testing"
"time"
@@ -36,8 +37,76 @@ func TestTrustCenter_LogoFileDownloadURL(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
trustCenterID := lookupTrustCenterID(t, owner)
activateTrustCenter(t, owner, trustCenterID)
organizationID := owner.GetOrganizationID().String()
const trustCenterQuery = `
query($organizationId: ID!) {
node(id: $organizationId) {
... on Organization {
trustCenter {
id
}
}
}
}
`
var trustCenterLookup struct {
Node struct {
TrustCenter struct {
ID string `json:"id"`
} `json:"trustCenter"`
} `json:"node"`
}
err := owner.Execute(trustCenterQuery, map[string]any{
"organizationId": organizationID,
}, &trustCenterLookup)
require.NoError(t, err)
require.NotEmpty(t, trustCenterLookup.Node.TrustCenter.ID)
trustCenterID := trustCenterLookup.Node.TrustCenter.ID
const activateMutation = `
mutation($input: UpdateTrustCenterInput!) {
updateTrustCenter(input: $input) {
trustCenter {
id
active
publicUrl
}
}
}
`
var activateResult struct {
UpdateTrustCenter struct {
TrustCenter struct {
ID string `json:"id"`
Active bool `json:"active"`
PublicURL string `json:"publicUrl"`
} `json:"trustCenter"`
} `json:"updateTrustCenter"`
}
err = owner.Execute(activateMutation, map[string]any{
"input": map[string]any{
"trustCenterId": trustCenterID,
"active": true,
},
}, &activateResult)
require.NoError(t, err)
// Publishing the page provisions a managed {slug}.probopage.localhost
// domain; the effective public URL resolves to it while no customer
// custom domain is primary.
require.NotEmpty(t, activateResult.UpdateTrustCenter.TrustCenter.PublicURL)
publicURL, err := url.Parse(activateResult.UpdateTrustCenter.TrustCenter.PublicURL)
require.NoError(t, err)
trustHost := publicURL.Host
require.NotEmpty(t, trustHost)
const uploadMutation = `
mutation UpdateTrustCenterBrand($input: UpdateTrustCenterBrandInput!) {
@@ -79,7 +148,7 @@ func TestTrustCenter_LogoFileDownloadURL(t *testing.T) {
} `json:"updateTrustCenterBrand"`
}
err := owner.ExecuteWithFile(uploadMutation, map[string]any{
err = owner.ExecuteWithFile(uploadMutation, map[string]any{
"input": map[string]any{
"trustCenterId": trustCenterID,
"logoFile": nil,
@@ -114,8 +183,18 @@ func TestTrustCenter_LogoFileDownloadURL(t *testing.T) {
} `json:"currentTrustCenter"`
}
err = owner.ExecuteTrust(trustCenterID, trustGraphQLQuery, nil, &trustResult)
require.NoError(t, err)
// The dedicated HTTPS listener only serves the page once the managed
// domain's certificate has been provisioned (async, ~1s poll in e2e), so
// retry until the TLS handshake and query succeed.
require.Eventually(t, func() bool {
trustResult.CurrentTrustCenter.Logo = nil
if err := owner.ExecuteTrust(trustHost, trustGraphQLQuery, nil, &trustResult); err != nil {
return false
}
return trustResult.CurrentTrustCenter.Logo != nil
}, 30*time.Second, 500*time.Millisecond, "trust center did not become servable on the dedicated listener")
require.NotNil(t, trustResult.CurrentTrustCenter.Logo)
assert.Equal(t, uploadResult.UpdateTrustCenterBrand.TrustCenter.Logo.ID, trustResult.CurrentTrustCenter.Logo.ID)

View File

@@ -49,59 +49,6 @@ export const description: INodeProperties[] = [
default: '',
description: 'The name of the organization',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
displayOptions: {
show: {
resource: ['organization'],
operation: ['update'],
},
},
default: '',
description: 'The description of the organization',
},
{
displayName: 'Website URL',
name: 'websiteUrl',
type: 'string',
displayOptions: {
show: {
resource: ['organization'],
operation: ['update'],
},
},
default: '',
description: 'The website URL of the organization',
},
{
displayName: 'Email',
name: 'email',
type: 'string',
placeholder: 'name@example.com',
displayOptions: {
show: {
resource: ['organization'],
operation: ['update'],
},
},
default: '',
description: 'The email address of the organization',
},
{
displayName: 'Headquarter Address',
name: 'headquarterAddress',
type: 'string',
displayOptions: {
show: {
resource: ['organization'],
operation: ['update'],
},
},
default: '',
description: 'The headquarter address of the organization',
},
];
export async function execute(
@@ -110,10 +57,6 @@ export async function execute(
): Promise<INodeExecutionData> {
const organizationId = this.getNodeParameter('organizationId', itemIndex) as string;
const name = this.getNodeParameter('name', itemIndex, '') as string;
const description = this.getNodeParameter('description', itemIndex, '') as string;
const websiteUrl = this.getNodeParameter('websiteUrl', itemIndex, '') as string;
const email = this.getNodeParameter('email', itemIndex, '') as string;
const headquarterAddress = this.getNodeParameter('headquarterAddress', itemIndex, '') as string;
const query = `
mutation UpdateOrganization($input: UpdateOrganizationInput!) {
@@ -121,10 +64,6 @@ export async function execute(
organization {
id
name
description
websiteUrl
email
headquarterAddress
logo {
id
fileName
@@ -144,10 +83,6 @@ export async function execute(
const input: Record<string, string> = { organizationId };
if (name) input.name = name;
if (description) input.description = description;
if (websiteUrl) input.websiteUrl = websiteUrl;
if (email) input.email = email;
if (headquarterAddress) input.headquarterAddress = headquarterAddress;
const responseData = await proboConnectApiRequest.call(this, query, { input });

View File

@@ -29,7 +29,7 @@ export const description: INodeProperties[] = [
displayOptions: {
show: {
resource: ['trustCenter'],
operation: ['createExternalUrl'],
operation: ['createCustomLink'],
},
},
default: '',
@@ -43,11 +43,11 @@ export const description: INodeProperties[] = [
displayOptions: {
show: {
resource: ['trustCenter'],
operation: ['createExternalUrl'],
operation: ['createCustomLink'],
},
},
default: '',
description: 'The name of the external URL',
description: 'The name of the custom link',
required: true,
},
{
@@ -57,11 +57,11 @@ export const description: INodeProperties[] = [
displayOptions: {
show: {
resource: ['trustCenter'],
operation: ['createExternalUrl'],
operation: ['createCustomLink'],
},
},
default: '',
description: 'The external URL',
description: 'The custom link',
required: true,
},
];
@@ -75,9 +75,9 @@ export async function execute(
const url = this.getNodeParameter('url', itemIndex) as string;
const query = `
mutation CreateComplianceExternalURL($input: CreateComplianceExternalURLInput!) {
createComplianceExternalURL(input: $input) {
complianceExternalURLEdge {
mutation CreateComplianceCustomLink($input: CreateComplianceCustomLinkInput!) {
createComplianceCustomLink(input: $input) {
complianceCustomLinkEdge {
node {
id
name

View File

@@ -23,17 +23,17 @@ import { proboApiRequest } from '../../GenericFunctions';
export const description: INodeProperties[] = [
{
displayName: 'Compliance External URL ID',
name: 'complianceExternalUrlId',
displayName: 'Compliance Custom Link ID',
name: 'complianceCustomLinkId',
type: 'string',
displayOptions: {
show: {
resource: ['trustCenter'],
operation: ['deleteExternalUrl'],
operation: ['deleteCustomLink'],
},
},
default: '',
description: 'The ID of the compliance external URL to delete',
description: 'The ID of the compliance custom link to delete',
required: true,
},
];
@@ -42,17 +42,17 @@ export async function execute(
this: IExecuteFunctions,
itemIndex: number,
): Promise<INodeExecutionData> {
const complianceExternalUrlId = this.getNodeParameter('complianceExternalUrlId', itemIndex) as string;
const complianceCustomLinkId = this.getNodeParameter('complianceCustomLinkId', itemIndex) as string;
const query = `
mutation DeleteComplianceExternalURL($input: DeleteComplianceExternalURLInput!) {
deleteComplianceExternalURL(input: $input) {
deletedComplianceExternalURLId
mutation DeleteComplianceCustomLink($input: DeleteComplianceCustomLinkInput!) {
deleteComplianceCustomLink(input: $input) {
deletedComplianceCustomLinkId
}
}
`;
const responseData = await proboApiRequest.call(this, query, { input: { id: complianceExternalUrlId } });
const responseData = await proboApiRequest.call(this, query, { input: { id: complianceCustomLinkId } });
return {
json: responseData,

View File

@@ -26,8 +26,8 @@ import * as createReferenceOp from './createReference.operation';
import * as deleteReferenceOp from './deleteReference.operation';
import * as getAllFilesOp from './getAllFiles.operation';
import * as deleteFileOp from './deleteFile.operation';
import * as createExternalUrlOp from './createExternalUrl.operation';
import * as deleteExternalUrlOp from './deleteExternalUrl.operation';
import * as createCustomLinkOp from './createCustomLink.operation';
import * as deleteCustomLinkOp from './deleteCustomLink.operation';
import * as getAllCommitmentGroupsOp from './getAllCommitmentGroups.operation';
import * as createCommitmentGroupOp from './createCommitmentGroup.operation';
import * as updateCommitmentGroupOp from './updateCommitmentGroup.operation';
@@ -62,10 +62,10 @@ export const description: INodeProperties[] = [
action: 'Create a compliance portal commitment group',
},
{
name: 'Create External URL',
value: 'createExternalUrl',
description: 'Create a new compliance external URL',
action: 'Create a compliance external URL',
name: 'Create Custom Link',
value: 'createCustomLink',
description: 'Create a new compliance custom link',
action: 'Create a compliance custom link',
},
{
name: 'Create Reference',
@@ -86,10 +86,10 @@ export const description: INodeProperties[] = [
action: 'Delete a compliance portal commitment group',
},
{
name: 'Delete External URL',
value: 'deleteExternalUrl',
description: 'Delete a compliance external URL',
action: 'Delete a compliance external URL',
name: 'Delete Custom Link',
value: 'deleteCustomLink',
description: 'Delete a compliance custom link',
action: 'Delete a compliance custom link',
},
{
name: 'Delete File',
@@ -161,8 +161,8 @@ export const description: INodeProperties[] = [
...deleteReferenceOp.description,
...getAllFilesOp.description,
...deleteFileOp.description,
...createExternalUrlOp.description,
...deleteExternalUrlOp.description,
...createCustomLinkOp.description,
...deleteCustomLinkOp.description,
...getAllCommitmentGroupsOp.description,
...createCommitmentGroupOp.description,
...updateCommitmentGroupOp.description,
@@ -181,8 +181,8 @@ export {
deleteReferenceOp as deleteReference,
getAllFilesOp as getAllFiles,
deleteFileOp as deleteFile,
createExternalUrlOp as createExternalUrl,
deleteExternalUrlOp as deleteExternalUrl,
createCustomLinkOp as createCustomLink,
deleteCustomLinkOp as deleteCustomLink,
getAllCommitmentGroupsOp as getAllCommitmentGroups,
createCommitmentGroupOp as createCommitmentGroup,
updateCommitmentGroupOp as updateCommitmentGroup,

View File

@@ -76,6 +76,59 @@ export const description: INodeProperties[] = [
default: '',
description: 'Whether search engines should index the trust center',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
displayOptions: {
show: {
resource: ['trustCenter'],
operation: ['update'],
},
},
default: '',
description: 'The description shown on the compliance page',
},
{
displayName: 'Website URL',
name: 'websiteUrl',
type: 'string',
displayOptions: {
show: {
resource: ['trustCenter'],
operation: ['update'],
},
},
default: '',
description: 'The website URL shown on the compliance page',
},
{
displayName: 'Email',
name: 'email',
type: 'string',
placeholder: 'name@example.com',
displayOptions: {
show: {
resource: ['trustCenter'],
operation: ['update'],
},
},
default: '',
description: 'The contact email shown on the compliance page',
},
{
displayName: 'Headquarter Address',
name: 'headquarterAddress',
type: 'string',
displayOptions: {
show: {
resource: ['trustCenter'],
operation: ['update'],
},
},
default: '',
description: 'The headquarter address shown on the compliance page',
},
];
export async function execute(
@@ -85,6 +138,10 @@ export async function execute(
const trustCenterId = this.getNodeParameter('trustCenterId', itemIndex) as string;
const active = this.getNodeParameter('active', itemIndex) as boolean | undefined;
const searchEngineIndexing = this.getNodeParameter('searchEngineIndexing', itemIndex, '') as string;
const description = this.getNodeParameter('description', itemIndex, '') as string;
const websiteUrl = this.getNodeParameter('websiteUrl', itemIndex, '') as string;
const email = this.getNodeParameter('email', itemIndex, '') as string;
const headquarterAddress = this.getNodeParameter('headquarterAddress', itemIndex, '') as string;
const query = `
mutation UpdateTrustCenter($input: UpdateTrustCenterInput!) {
@@ -93,6 +150,10 @@ export async function execute(
id
active
searchEngineIndexing
description
websiteUrl
email
headquarterAddress
createdAt
updatedAt
}
@@ -103,6 +164,10 @@ export async function execute(
const input: Record<string, unknown> = { trustCenterId };
if (active !== undefined) input.active = active;
if (searchEngineIndexing) input.searchEngineIndexing = searchEngineIndexing;
if (description) input.description = description;
if (websiteUrl) input.websiteUrl = websiteUrl;
if (email) input.email = email;
if (headquarterAddress) input.headquarterAddress = headquarterAddress;
const responseData = await proboApiRequest.call(this, query, { input });