Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/e2e/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func (suite *basicSuite) TestTenantsAPI() {
Body: map[string]interface{}{
"id": tenantID,
"destinations_count": 1,
"topics": suite.config.Topics,
"topics": []string{"*"},
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion internal/apirouter/tenant_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func (h *TenantHandlers) RetrievePortal(c *gin.Context) {
theme = ""
}

portalURL := scheme + "://" + c.Request.Host + "?token=" + jwtToken + "&tenant_id=" + tenant.ID
portalURL := scheme + "://" + c.Request.Host + "?token=" + jwtToken
if theme != "" {
portalURL += "&theme=" + theme
}
Expand Down
6 changes: 3 additions & 3 deletions internal/apirouter/tenant_handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func TestTenantRetrievePortalHandler(t *testing.T) {
router, _, redisClient := setupTestRouter(t, apiKey, jwtSecret)
entityStore := setupTestEntityStore(t, redisClient, nil)

t.Run("should return redirect_url with tenant_id and tenant_id in body", func(t *testing.T) {
t.Run("should return redirect_url with token and tenant_id in body", func(t *testing.T) {
t.Parallel()

// Setup
Expand All @@ -261,7 +261,7 @@ func TestTenantRetrievePortalHandler(t *testing.T) {
// Test
assert.Equal(t, http.StatusOK, w.Code)
assert.NotEmpty(t, response["redirect_url"])
assert.Contains(t, response["redirect_url"], "tenant_id="+existingResource.ID)
assert.Contains(t, response["redirect_url"], "token=")
assert.Equal(t, existingResource.ID, response["tenant_id"])

// Cleanup
Expand All @@ -288,7 +288,7 @@ func TestTenantRetrievePortalHandler(t *testing.T) {

// Test
assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, response["redirect_url"], "tenant_id="+existingResource.ID)
assert.Contains(t, response["redirect_url"], "token=")
assert.Contains(t, response["redirect_url"], "theme=dark")
assert.Equal(t, existingResource.ID, response["tenant_id"])

Expand Down
6 changes: 6 additions & 0 deletions internal/portal/src/common/TopicPicker/TopicPicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,10 @@
font-size: var(--font-size-m);
line-height: var(--line-height-m);
}

&__flat-topic {
padding-left: var(--spacing-6);
margin-top: var(--spacing-3);
margin-bottom: var(--spacing-3);
}
}
63 changes: 39 additions & 24 deletions internal/portal/src/common/TopicPicker/TopicPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,32 @@ interface TopicPickerProps {
onTopicsChange: (topics: string[]) => void;
}

const detectSeparator = (topics: string[]): string => {
// Common separators to check
const possibleSeparators = ["/", ".", "-"];
const possibleSeparators = ["/", ".", "-"];

// Find the first separator that appears in all topics
// and is the first occurring separator in each topic
return (
possibleSeparators.find((sep) =>
topics.every((topic) => {
const sepIndex = topic.indexOf(sep);
if (sepIndex === -1) return false;

// Check if any other separator appears before this one
const otherSepsIndex = possibleSeparators
.filter((s) => s !== sep)
.map((s) => topic.indexOf(s))
.filter((idx) => idx !== -1);

return otherSepsIndex.every((idx) => idx === -1 || idx > sepIndex);
}),
) || "-"
); // Fallback to '-' if no consistent separator is found
const findFirstSeparator = (topic: string): string | null => {
let firstIndex = -1;
let firstSep: string | null = null;

for (const sep of possibleSeparators) {
const idx = topic.indexOf(sep);
if (idx !== -1 && (firstIndex === -1 || idx < firstIndex)) {
firstIndex = idx;
firstSep = sep;
}
}

return firstSep;
};

const topics: Topic[] = (() => {
const topicsList = CONFIGS.TOPICS.split(",");
const separator = detectSeparator(topicsList);

return topicsList.map((topic) => {
const parts = topic.split(separator);
const separator = findFirstSeparator(topic);

return {
id: topic,
category: parts[0],
category: separator ? topic.split(separator)[0] : topic,
};
});
})();
Expand Down Expand Up @@ -162,6 +155,28 @@ const TopicPicker = ({
const areAllSelected = selectedCount === categoryTopics.length;
const isIndeterminate = selectedCount > 0 && !areAllSelected;

// Check if this is a flat topic (no actual nesting)
const isFlatTopic =
categoryTopics.length === 1 && categoryTopics[0].id === category;

if (isFlatTopic) {
const topic = categoryTopics[0];
return (
<div key={category} className="topic-picker__flat-topic">
<Checkbox
checked={
isEverythingSelected ||
selectedTopics.indexOf(topic.id) !== -1
}
onChange={() => toggleTopic(topic.id)}
label={topic.id}
monospace
disabled={isEverythingSelected}
/>
</div>
);
}

return (
<div key={category} className="topic-picker__category">
<div className="topic-picker__category-header">
Expand Down
12 changes: 8 additions & 4 deletions internal/portal/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import react from "@vitejs/plugin-react-swc";
import { sentryVitePlugin } from "@sentry/vite-plugin";

export default defineConfig(({ mode }) => {
let plugins: UserConfig["plugins"] = [react()];
const plugins: UserConfig["plugins"] = [react()];

if (process.env.SENTRY_AUTH_TOKEN && mode === "production") {
plugins.push(
Expand All @@ -28,9 +28,13 @@ export default defineConfig(({ mode }) => {
plugins,
server: {
port: 3334,
// hmr: {
// port: 3334,
// },
host: true,
watch: {
usePolling: true, // Required for Docker volume mounts
},
hmr: {
clientPort: 3334, // Ensure HMR WebSocket connects to the right port
},
},
build: {
sourcemap: true,
Expand Down