Compare commits

..

5 Commits

Author SHA1 Message Date
Karan Balani
3ec91fcc19 fix: types for email and org id in storableuser struct 2026-03-18 02:31:52 +05:30
Karan Balani
53e46a3a2c chore: use deleted at also in conversions and remove user_role file, will be added in diff pr 2026-03-18 02:10:15 +05:30
Karan Balani
756c3274ff chore: revert openapi changes, keeping this clean 2026-03-18 01:57:24 +05:30
Karan Balani
05cd026983 chore: update openapi spec 2026-03-18 01:21:15 +05:30
Karan Balani
1770ded9df refactor: separate db and domain models for user 2026-03-17 22:44:13 +05:30
51 changed files with 190 additions and 1178 deletions

View File

@@ -308,9 +308,6 @@ user:
allow_self: true
# The duration within which a user can reset their password.
max_token_lifetime: 6h
invite:
# The duration within which a user can accept their invite.
max_token_lifetime: 48h
root:
# Whether to enable the root user. When enabled, a root user is provisioned
# on startup using the email and password below. The root user cannot be

View File

@@ -190,7 +190,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:v0.116.0
image: signoz/signoz:v0.115.0
ports:
- "8080:8080" # signoz port
# - "6060:6060" # pprof port

View File

@@ -117,7 +117,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:v0.116.0
image: signoz/signoz:v0.115.0
ports:
- "8080:8080" # signoz port
volumes:

View File

@@ -181,7 +181,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:${VERSION:-v0.116.0}
image: signoz/signoz:${VERSION:-v0.115.0}
container_name: signoz
ports:
- "8080:8080" # signoz port

View File

@@ -109,7 +109,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:${VERSION:-v0.116.0}
image: signoz/signoz:${VERSION:-v0.115.0}
container_name: signoz
ports:
- "8080:8080" # signoz port

View File

@@ -1,4 +0,0 @@
module: "github.com/signoz/signoz/schemas"
language: {
version: "v0.12.0"
}

View File

@@ -1,4 +0,0 @@
docker rm -f perses
rm mf-manifest.json
rm package.json
rm signoz-0.0.1.tar.gz

View File

@@ -1,185 +0,0 @@
{
"description": "This dashboard shows the Redis instance overview. It includes latency, hit/miss rate, connections, and memory information.\n",
"id": "redis-overview",
"layout": [
{
"h": 3,
"i": "a77227c7-16f5-4353-952e-b183c715a61c",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 0
},
{
"h": 3,
"i": "bf0deeeb-e926-4234-944c-82bacd96af47",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 0
}
],
"tags": [
"redis",
"database"
],
"title": "Redis overview",
"variables": {
"94f19b3c-ad9f-4b47-a9b2-f312c09fa965": {
"allSelected": true,
"customValue": "",
"description": "List of hosts sending Redis metrics",
"id": "94f19b3c-ad9f-4b47-a9b2-f312c09fa965",
"key": "94f19b3c-ad9f-4b47-a9b2-f312c09fa965",
"modificationUUID": "4c5b0c03-9cbc-425b-8d8e-7152e5c39ba8",
"multiSelect": true,
"name": "host_name",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'host_name') AS host_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'redis_cpu_time'\nGROUP BY host_name",
"selectedValue": [
"Srikanths-MacBook-Pro.local"
],
"showALLOption": true,
"sort": "ASC",
"textboxValue": "",
"type": "QUERY"
}
},
"version": "v5",
"widgets": [
{
"description": "Rate successful lookup of keys in the main dictionary",
"fillSpans": false,
"id": "a77227c7-16f5-4353-952e-b183c715a61c",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_keyspace_hits",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
},
"groupBy": [],
"having": {
"expression": ""
},
"legend": "Hit/s across all hosts",
"limit": null,
"orderBy": [],
"queryName": "A",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "42c9c117-bfaf-49f7-b528-aad099392295",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Hits/s",
"yAxisUnit": "none"
},
{
"description": "Number of clients pending on a blocking call",
"fillSpans": false,
"id": "bf0deeeb-e926-4234-944c-82bacd96af47",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_clients_blocked",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
},
"groupBy": [],
"having": {
"expression": ""
},
"legend": "Blocked clients across all hosts",
"limit": null,
"orderBy": [],
"queryName": "A",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "6b48fc1c-014a-45e6-a4ce-2c0e06a23302",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Blocked clients",
"yAxisUnit": "none"
}
]
}

View File

@@ -1,160 +0,0 @@
{
"kind": "Dashboard",
"metadata": {
"name": "redis-overview",
"project": "signoz"
},
"spec": {
"display": {
"name": "Redis overview",
"description": "This dashboard shows the Redis instance overview. It includes latency, hit/miss rate, connections, and memory information."
},
"duration": "1h",
"variables": [
{
"kind": "ListVariable",
"spec": {
"name": "host_name",
"display": {
"name": "host_name",
"description": "List of hosts sending Redis metrics"
},
"allowAllValue": true,
"allowMultiple": true,
"plugin": {
"kind": "SigNozQueryVariable",
"spec": {
"queryValue": "SELECT JSONExtractString(labels, 'host_name') AS host_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'redis_cpu_time'\nGROUP BY host_name",
"sort": "ASC",
"multiSelect": true,
"showALLOption": true
}
}
}
}
],
"panels": {
"a77227c7": {
"kind": "Panel",
"spec": {
"display": {
"name": "Hits/s",
"description": "Rate successful lookup of keys in the main dictionary"
},
"plugin": {
"kind": "TimeSeriesChart",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "metrics",
"aggregations": [
{
"metricName": "redis_keyspace_hits",
"reduceTo": "sum",
"spaceAggregation": "sum",
"timeAggregation": "rate"
}
],
"filter": {
"expression": "host_name IN $host_name"
},
"groupBy": [],
"order": [],
"expression": "A",
"disabled": false,
"legend": "Hit/s across all hosts",
"stepInterval": 60,
"having": {
"expression": ""
}
}
}
}
}
]
}
},
"bf0deeeb": {
"kind": "Panel",
"spec": {
"display": {
"name": "Blocked clients",
"description": "Number of clients pending on a blocking call"
},
"plugin": {
"kind": "TimeSeriesChart",
"spec": {}
},
"queries": [
{
"kind": "TimeSeriesQuery",
"spec": {
"plugin": {
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "metrics",
"aggregations": [
{
"metricName": "redis_clients_blocked",
"reduceTo": "sum",
"spaceAggregation": "sum",
"timeAggregation": "sum"
}
],
"filter": {
"expression": "host_name IN $host_name"
},
"groupBy": [],
"order": [],
"expression": "A",
"disabled": false,
"legend": "Blocked clients across all hosts",
"stepInterval": 60,
"having": {
"expression": ""
}
}
}
}
}
]
}
}
},
"layouts": [
{
"kind": "Grid",
"spec": {
"items": [
{
"x": 0,
"y": 0,
"width": 6,
"height": 3,
"content": {
"$ref": "#/spec/panels/a77227c7"
}
},
{
"x": 6,
"y": 0,
"width": 6,
"height": 3,
"content": {
"$ref": "#/spec/panels/bf0deeeb"
}
}
]
}
}
]
}
}

View File

@@ -1,149 +0,0 @@
#!/usr/bin/env python3
"""
Generates package.json and mf-manifest.json for a Perses plugin module
by reading the kind: "..." declarations from your CUE schema files,
then builds the archive ready to mount into Perses.
Usage (run from the root of your plugin folder, where schemas/ lives):
python3 generate_manifests.py --org signoz --name signoz --version 0.0.1
"""
import os
import re
import json
import tarfile
import argparse
# Maps keywords in schema folder names to Perses plugin kinds.
# Add more here if you introduce new plugin types.
KIND_HINTS = {
"datasource": "Datasource",
"variable": "Variable",
"promql": "TimeSeriesQuery",
"formula": "TimeSeriesQuery",
"join": "TimeSeriesQuery",
"sql": "TimeSeriesQuery",
"composite": "TimeSeriesQuery",
"builder": "TimeSeriesQuery",
"query": "TimeSeriesQuery",
"panel": "Panel",
"trace": "TraceQuery",
"log": "LogQuery",
"profile": "ProfileQuery",
}
def infer_kind(folder_name):
lower = folder_name.lower()
for hint, kind in KIND_HINTS.items():
if hint in lower:
return kind
return None
def extract_kind_from_cue(cue_file):
"""Extract kind: "PluginName" from a CUE file."""
with open(cue_file) as f:
content = f.read()
match = re.search(r'^\s*kind:\s*"([^"]+)"', content, re.MULTILINE)
return match.group(1) if match else None
def to_display_name(name):
"""Convert CamelCase to 'Camel Case'."""
return re.sub(r'(?<=[a-z])(?=[A-Z])', ' ', name)
def main():
parser = argparse.ArgumentParser(description="Generate Perses plugin manifests from CUE files and build archive.")
parser.add_argument("--org", required=True, help="Your org name, e.g. signoz")
parser.add_argument("--name", required=True, help="Plugin module name, e.g. signoz")
parser.add_argument("--version", default="0.0.1", help="Plugin version, e.g. 0.0.1")
parser.add_argument("--schemas-dir", default="schemas", help="Path to schemas directory (default: schemas)")
args = parser.parse_args()
schemas_dir = args.schemas_dir
if not os.path.isdir(schemas_dir):
print(f"Error: schemas directory '{schemas_dir}' not found. Run this script from your plugin root folder.")
exit(1)
plugins = []
for folder in sorted(os.listdir(schemas_dir)):
folder_path = os.path.join(schemas_dir, folder)
if not os.path.isdir(folder_path):
continue
plugin_kind_name = None
cue_file = os.path.join(folder_path, f"{folder}.cue")
if os.path.isfile(cue_file):
plugin_kind_name = extract_kind_from_cue(cue_file)
perses_kind = infer_kind(folder)
if not plugin_kind_name:
print(f"Warning: could not extract kind from '{cue_file}', skipping.")
continue
if not perses_kind:
print(f"Warning: could not infer Perses kind for folder '{folder}', skipping. Add a hint to KIND_HINTS.")
continue
plugins.append({
"kind": perses_kind,
"spec": {
"display": {"name": to_display_name(plugin_kind_name)},
"name": plugin_kind_name
}
})
print(f"Found: {plugin_kind_name} -> {perses_kind}")
if not plugins:
print("No plugins found. Check that your schemas directory contains CUE files with kind: declarations.")
exit(1)
# Generate mf-manifest.json
manifest = {
"id": args.name,
"name": args.name,
"metaData": {
"buildInfo": {
"buildVersion": args.version
}
},
"plugins": [
{"kind": p["kind"], "name": p["spec"]["name"]}
for p in plugins
]
}
# Generate package.json
package = {
"name": f"@{args.org}/{args.name}",
"version": args.version,
"description": f"{args.name} plugin module for Perses",
"perses": {
"schemasPath": "schemas",
"plugins": plugins
}
}
with open("mf-manifest.json", "w") as f:
json.dump(manifest, f, indent=2)
print("\nWrote mf-manifest.json")
with open("package.json", "w") as f:
json.dump(package, f, indent=2)
print("Wrote package.json")
# Build the archive
archive_name = f"{args.name}-{args.version}.tar.gz"
with tarfile.open(archive_name, "w:gz") as tar:
tar.add("package.json")
tar.add("mf-manifest.json")
if os.path.isdir("schemas"):
tar.add("schemas")
if os.path.isdir("cue.mod"):
tar.add("cue.mod")
print(f"Wrote {archive_name}")
print(f"\nDone! {len(plugins)} plugin(s) packaged:")
for p in plugins:
print(f" - {p['spec']['name']} ({p['kind']})")
if __name__ == "__main__":
main()

View File

@@ -1,27 +0,0 @@
Panel Plugins
All SigNoz panels use Perses built-in panel kinds directly. No custom CUE
schemas exist yet because Perses does not publish CUE schemas for its panel
plugins (only Go SDK + TypeScript). Once it does, each panel can embed the
upstream spec and add the SigNoz-specific fields listed below.
SigNoz-specific fields by panel type:
TimeSeriesChart timePreference
StatChart timePreference, contextLinks
BarChart timePreference, contextLinks
PieChart timePreference, contextLinks
Table timePreference, contextLinks
LogsTable (List) timePreference, selectedLogFields, selectedTracesFields, columnWidths
TraceTable timePreference, selectedTracesFields, columnWidths
HistogramChart timePreference, contextLinks, bucketCount, bucketWidth, mergeAllActiveQueries
Common fields:
timePreference — panel-local vs dashboard-global time range
contextLinks — clickable drill-down links on data points
Panel-specific fields:
selectedLogFields / selectedTracesFields — which fields to display as columns in list views
columnWidths — saved column width overrides
bucketCount / bucketWidth — histogram bucket configuration
mergeAllActiveQueries — combine multiple queries into one histogram

View File

@@ -1,84 +0,0 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/builder_query.go — QueryBuilderQuery
kind: "SigNozBuilderQuery"
spec: close({
name: #QueryName
signal: "metrics" | "logs" | "traces"
expression: string
disabled?: bool | *false
// Metrics use structured aggregations; logs/traces use expression-based.
aggregations?: [...#MetricAggregation]
expressionAggregations?: [...#ExpressionAggregation]
filter?: #FilterExpression
groupBy?: [...#GroupByItem]
order?: [...#OrderByItem]
selectFields?: [...]
limit?: #Limit
limitBy?: #LimitBy
offset?: #Offset
cursor?: string
having?: #HavingExpression
// secondaryAggregations not added — not yet implemented.
functions?: [...#Function]
legend?: string
stepInterval?: number
reduceTo?: #ReduceTo
pageSize?: int & >=1
source?: string
})
#LimitBy: close({
keys: [...string]
value: string
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"
#ReduceTo: "sum" | "count" | "avg" | "min" | "max" | "last" | "median"
#Limit: int & >=0 & <=10000
#Offset: int & >=0
#MetricAggregation: close({
metricName: string & !=""
timeAggregation: "latest" | "sum" | "avg" | "min" | "max" | "count" | "rate" | "increase"
spaceAggregation: "sum" | "avg" | "min" | "max" | "count" | "p50" | "p75" | "p90" | "p95" | "p99"
reduceTo?: #ReduceTo
temporality?: "delta" | "cumulative" | "unspecified"
})
#ExpressionAggregation: close({
expression: string & !=""
alias?: string
})
#FilterExpression: close({
expression: string
})
#GroupByItem: close({
name: string & !=""
fieldDataType?: string
fieldContext?: string
})
#OrderByItem: close({
columnName: string & !=""
order: "asc" | "desc"
})
#HavingExpression: close({
expression: string
})
#Function: close({
name: "cutOffMin" | "cutOffMax" | "clampMin" | "clampMax" |
"absolute" | "runningDiff" | "log2" | "log10" |
"cumulativeSum" | "ewma3" | "ewma5" | "ewma7" |
"median3" | "median5" | "median7" | "timeShift" |
"anomaly" | "fillZero"
args?: [...close({value: number | string | bool})]
})

View File

@@ -1,24 +0,0 @@
{
"kind": "SigNozBuilderQuery",
"spec": {
"name": "A",
"signal": "metrics",
"expression": "A",
"aggregations": [
{
"metricName": "redis_keyspace_hits",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "sum"
}
],
"filter": {
"expression": "host_name IN $host_name"
},
"groupBy": [],
"order": [],
"disabled": false,
"legend": "Hit/s across all hosts",
"stepInterval": 60
}
}

View File

@@ -1,12 +0,0 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/clickhouse_query.go — ClickHouseQuery
kind: "SigNozClickHouseSQL"
spec: close({
name: #QueryName
query: string & !=""
disabled?: bool | *false
legend?: string
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"

View File

@@ -1,9 +0,0 @@
{
"kind": "SigNozClickHouseSQL",
"spec": {
"name": "A",
"query": "SELECT toStartOfInterval(timestamp, INTERVAL 1 MINUTE) AS ts, count() AS total FROM signoz_logs.distributed_logs GROUP BY ts ORDER BY ts",
"disabled": false,
"legend": "Log count"
}
}

View File

@@ -1,24 +0,0 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/req.go — CompositeQuery
// SigNozCompositeQuery groups multiple query plugins into a single
// query request. Each entry is a typed envelope whose spec is
// validated by the corresponding plugin schema.
kind: "SigNozCompositeQuery"
spec: close({
queries: [...#QueryEnvelope]
})
// QueryEnvelope wraps a single query plugin with a type discriminator.
#QueryEnvelope: close({
type: #QueryType
spec: {...}
})
#QueryType:
"builder_query" |
"builder_formula" |
"builder_join" |
"builder_trace_operator" |
"promql" |
"clickhouse_sql"

View File

@@ -1,53 +0,0 @@
{
"kind": "SigNozCompositeQuery",
"spec": {
"queries": [
{
"type": "builder_query",
"spec": {
"name": "A",
"signal": "metrics",
"expression": "A",
"aggregations": [
{
"metricName": "redis_keyspace_hits",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "sum"
}
],
"filter": {
"expression": "host_name IN $host_name"
}
}
},
{
"type": "builder_query",
"spec": {
"name": "B",
"signal": "metrics",
"expression": "B",
"aggregations": [
{
"metricName": "redis_keyspace_misses",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "sum"
}
],
"filter": {
"expression": "host_name IN $host_name"
}
}
},
{
"type": "builder_formula",
"spec": {
"name": "F1",
"expression": "A / (A + B) * 100",
"legend": "Hit rate %"
}
}
]
}
}

View File

@@ -1,12 +0,0 @@
package model
// defaultValue lives on the Perses ListVariable wrapper (spec level).
kind: "SigNozCustomVariable"
spec: close({
customValue: string
sort?: #VariableSortOrder
multiSelect?: bool
showALLOption?: bool
})
#VariableSortOrder: *"DISABLED" | "ASC" | "DESC"

View File

@@ -1,8 +0,0 @@
{
"kind": "SigNozCustomVariable",
"spec": {
"customValue": "production,staging,development",
"sort": "DISABLED",
"multiSelect": false
}
}

View File

@@ -1,9 +0,0 @@
package model
kind: "SigNozDatasource"
// SigNoz has a single built-in backend — the frontend already knows
// the API endpoint, so there is no connection config to validate.
// Add fields here if SigNoz ever supports multiple backends or
// configurable API versions.
spec: close({})

View File

@@ -1,4 +0,0 @@
{
"kind": "SigNozDatasource",
"spec": {}
}

View File

@@ -1,13 +0,0 @@
package model
// defaultValue lives on the Perses ListVariable wrapper (spec level).
kind: "SigNozDynamicVariable"
spec: close({
dynamicVariablesAttribute: string
dynamicVariablesSource: string
sort?: #VariableSortOrder
multiSelect?: bool
showALLOption?: bool
})
#VariableSortOrder: *"DISABLED" | "ASC" | "DESC"

View File

@@ -1,10 +0,0 @@
{
"kind": "SigNozDynamicVariable",
"spec": {
"dynamicVariablesAttribute": "host_name",
"dynamicVariablesSource": "metrics",
"sort": "ASC",
"multiSelect": true,
"showALLOption": true
}
}

View File

@@ -1,27 +0,0 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/formula.go — QueryBuilderFormula
kind: "SigNozFormula"
spec: close({
name: #QueryName
expression: string
disabled?: bool | *false
legend?: string
limit?: #Limit
having?: #HavingExpression
stepInterval?: number
order?: [...#OrderByItem]
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"
#Limit: int & >=0 & <=10000
#HavingExpression: close({
expression: string
})
#OrderByItem: close({
columnName: string & !=""
order: "asc" | "desc"
})

View File

@@ -1,8 +0,0 @@
{
"kind": "SigNozFormula",
"spec": {
"name": "F1",
"expression": "A / B * 100",
"legend": "Hit rate %"
}
}

View File

@@ -1,75 +0,0 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/join.go — QueryBuilderJoin
kind: "SigNozJoin"
spec: close({
name: #QueryName
left: #QueryRef
right: #QueryRef
type: #JoinType
on: string
disabled?: bool | *false
aggregations?: [...#MetricAggregation]
expressionAggregations?: [...#ExpressionAggregation]
selectFields?: [...]
filter?: #FilterExpression
groupBy?: [...#GroupByItem]
having?: #HavingExpression
// secondaryAggregations not added — not yet implemented.
order?: [...#OrderByItem]
limit?: #Limit
functions?: [...#Function]
})
#QueryRef: close({
name: #QueryName
})
#JoinType: "inner" | "left" | "right" | "full" | "cross"
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"
#ReduceTo: "sum" | "count" | "avg" | "min" | "max" | "last" | "median"
#Limit: int & >=0 & <=10000
#MetricAggregation: close({
metricName: string & !=""
timeAggregation: "latest" | "sum" | "avg" | "min" | "max" | "count" | "rate" | "increase"
spaceAggregation: "sum" | "avg" | "min" | "max" | "count" | "p50" | "p75" | "p90" | "p95" | "p99"
reduceTo?: #ReduceTo
temporality?: "delta" | "cumulative" | "unspecified"
})
#ExpressionAggregation: close({
expression: string & !=""
alias?: string
})
#FilterExpression: close({
expression: string
})
#GroupByItem: close({
name: string & !=""
fieldDataType?: string
fieldContext?: string
})
#OrderByItem: close({
columnName: string & !=""
order: "asc" | "desc"
})
#HavingExpression: close({
expression: string
})
#Function: close({
name: "cutOffMin" | "cutOffMax" | "clampMin" | "clampMax" |
"absolute" | "runningDiff" | "log2" | "log10" |
"cumulativeSum" | "ewma3" | "ewma5" | "ewma7" |
"median3" | "median5" | "median7" | "timeShift" |
"anomaly" | "fillZero"
args?: [...close({value: number | string | bool})]
})

View File

@@ -1,11 +0,0 @@
{
"kind": "SigNozJoin",
"spec": {
"name": "J1",
"left": {"name": "A"},
"right": {"name": "B"},
"type": "inner",
"on": "service.name = service.name",
"disabled": false
}
}

View File

@@ -1,14 +0,0 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/prom_query.go — PromQuery
kind: "SigNozPromQL"
spec: close({
name: #QueryName
query: string & !=""
disabled?: bool | *false
step?: number
stats?: bool
legend?: string
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"

View File

@@ -1,9 +0,0 @@
{
"kind": "SigNozPromQL",
"spec": {
"name": "A",
"query": "rate(http_requests_total{status=\"200\"}[5m])",
"disabled": false,
"legend": "{{method}} {{path}}"
}
}

View File

@@ -1,12 +0,0 @@
package model
// defaultValue lives on the Perses ListVariable wrapper (spec level).
kind: "SigNozQueryVariable"
spec: close({
queryValue: string
sort?: #VariableSortOrder
multiSelect?: bool
showALLOption?: bool
})
#VariableSortOrder: *"DISABLED" | "ASC" | "DESC"

View File

@@ -1,9 +0,0 @@
{
"kind": "SigNozQueryVariable",
"spec": {
"queryValue": "SELECT DISTINCT host_name FROM signoz_metrics.distributed_time_series_v4_1day WHERE metric_name = 'redis_cpu_time'",
"sort": "ASC",
"multiSelect": true,
"showALLOption": true
}
}

View File

@@ -1,68 +0,0 @@
package model
// Source: pkg/types/querybuildertypes/querybuildertypesv5/trace_operator.go — QueryBuilderTraceOperator
// SigNozTraceOperator composes multiple trace BuilderQueries using
// relational operators (=>, ->, &&, ||, NOT) to query trace relationships.
// Signal is implicitly "traces" — all referenced queries must be trace queries.
kind: "SigNozTraceOperator"
spec: close({
name: #QueryName
// Operator expression composing trace queries, e.g. "A => B && C".
expression: string & !=""
disabled?: bool | *false
// Which query's spans to return (must be a query referenced in expression).
returnSpansFrom?: #QueryName
aggregations?: [...#ExpressionAggregation]
filter?: #FilterExpression
groupBy?: [...#GroupByItem]
order?: [...#OrderByItem]
limit?: #Limit
offset?: #Offset
cursor?: string
functions?: [...#Function]
stepInterval?: number
having?: #HavingExpression
legend?: string
selectFields?: [...]
})
#QueryName: =~"^[A-Za-z][A-Za-z0-9_]*$"
#Limit: int & >=0 & <=10000
#Offset: int & >=0
#ExpressionAggregation: close({
expression: string & !=""
alias?: string
})
#FilterExpression: close({
expression: string
})
#GroupByItem: close({
name: string & !=""
fieldDataType?: string
fieldContext?: string
})
#OrderByItem: close({
columnName: string & !=""
order: "asc" | "desc"
})
#HavingExpression: close({
expression: string
})
#Function: close({
name: "cutOffMin" | "cutOffMax" | "clampMin" | "clampMax" |
"absolute" | "runningDiff" | "log2" | "log10" |
"cumulativeSum" | "ewma3" | "ewma5" | "ewma7" |
"median3" | "median5" | "median7" | "timeShift" |
"anomaly" | "fillZero"
args?: [...close({value: number | string | bool})]
})

View File

@@ -1,19 +0,0 @@
{
"kind": "SigNozTraceOperator",
"spec": {
"name": "T1",
"expression": "A => B",
"returnSpansFrom": "A",
"aggregations": [
{
"expression": "count()",
"alias": "request_count"
}
],
"filter": {
"expression": "service.name = 'frontend'"
},
"groupBy": [],
"order": []
}
}

View File

@@ -1,2 +0,0 @@
python3 generate_manifests.py --org signoz --name signoz --version 0.0.1
docker run -d -p 8080:8080 --name perses -v $(pwd)/signoz-0.0.1.tar.gz:/etc/perses/plugins-archive/signoz-plugin.tar.gz persesdev/perses:latest-debug

View File

@@ -1,4 +0,0 @@
./setup.sh
sleep 2
./validate.sh
./deletePostValidate.sh

View File

@@ -1 +0,0 @@
percli lint -f ./examples/redis-overview-perses.json --online

View File

@@ -186,7 +186,7 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
Description: "This endpoint lists all users",
Request: nil,
RequestContentType: "",
Response: make([]*types.GettableUser, 0),
Response: make([]*types.User, 0),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{},
@@ -203,7 +203,7 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
Description: "This endpoint returns the user I belong to",
Request: nil,
RequestContentType: "",
Response: new(types.GettableUser),
Response: new(types.User),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{},
@@ -220,7 +220,7 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
Description: "This endpoint returns the user by id",
Request: nil,
RequestContentType: "",
Response: new(types.GettableUser),
Response: new(types.User),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusNotFound},
@@ -237,7 +237,7 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
Description: "This endpoint updates the user by id",
Request: new(types.User),
RequestContentType: "application/json",
Response: new(types.GettableUser),
Response: new(types.User),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},

View File

@@ -17,8 +17,8 @@ func NewStore(sqlstore sqlstore.SQLStore) authtypes.AuthNStore {
return &store{sqlstore: sqlstore}
}
func (store *store) GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx context.Context, email string, orgID valuer.UUID) (*types.User, *types.FactorPassword, error) {
user := new(types.User)
func (store *store) GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx context.Context, email string, orgID valuer.UUID) (*types.StorableUser, *types.FactorPassword, error) {
user := new(types.StorableUser)
factorPassword := new(types.FactorPassword)
err := store.

View File

@@ -30,7 +30,7 @@ func (module *module) Create(ctx context.Context, timestamp int64, name string,
funnel.CreatedBy = userID.String()
// Set up the user relationship
funnel.CreatedByUser = &types.User{
funnel.CreatedByUser = &types.StorableUser{
Identifiable: types.Identifiable{
ID: userID,
},

View File

@@ -27,12 +27,7 @@ type OrgConfig struct {
}
type PasswordConfig struct {
Invite InviteConfig `mapstructure:"invite"`
Reset ResetConfig `mapstructure:"reset"`
}
type InviteConfig struct {
MaxTokenLifetime time.Duration `mapstructure:"max_token_lifetime"`
Reset ResetConfig `mapstructure:"reset"`
}
type ResetConfig struct {
@@ -51,9 +46,6 @@ func newConfig() factory.Config {
AllowSelf: false,
MaxTokenLifetime: 6 * time.Hour,
},
Invite: InviteConfig{
MaxTokenLifetime: 48 * time.Hour,
},
},
Root: RootConfig{
Enabled: false,
@@ -69,10 +61,6 @@ func (c Config) Validate() error {
return errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "user::password::reset::max_token_lifetime must be positive")
}
if c.Password.Invite.MaxTokenLifetime <= 0 {
return errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "user::password::invite::max_token_lifetime must be positive")
}
if c.Root.Enabled {
if c.Root.Email.IsZero() {
return errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "user::root::email is required when root user is enabled")

View File

@@ -21,11 +21,15 @@ func NewGetter(store types.UserStore, flagger flagger.Flagger) user.Getter {
}
func (module *getter) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*types.User, error) {
return module.store.GetRootUserByOrgID(ctx, orgID)
storableUser, err := module.store.GetRootUserByOrgID(ctx, orgID)
if err != nil {
return nil, err
}
return types.NewUserFromStorable(storableUser), nil
}
func (module *getter) ListByOrgID(ctx context.Context, orgID valuer.UUID) ([]*types.User, error) {
users, err := module.store.ListUsersByOrgID(ctx, orgID)
storableUsers, err := module.store.ListUsersByOrgID(ctx, orgID)
if err != nil {
return nil, err
}
@@ -35,46 +39,46 @@ func (module *getter) ListByOrgID(ctx context.Context, orgID valuer.UUID) ([]*ty
hideRootUsers := module.flagger.BooleanOrEmpty(ctx, flagger.FeatureHideRootUser, evalCtx)
if hideRootUsers {
users = slices.DeleteFunc(users, func(user *types.User) bool { return user.IsRoot })
storableUsers = slices.DeleteFunc(storableUsers, func(user *types.StorableUser) bool { return user.IsRoot })
}
return users, nil
return types.NewUsersFromStorables(storableUsers), nil
}
func (module *getter) GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*types.User, error) {
users, err := module.store.GetUsersByEmail(ctx, email)
storableUsers, err := module.store.GetUsersByEmail(ctx, email)
if err != nil {
return nil, err
}
return users, nil
return types.NewUsersFromStorables(storableUsers), nil
}
func (module *getter) GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*types.User, error) {
user, err := module.store.GetByOrgIDAndID(ctx, orgID, id)
storableUser, err := module.store.GetByOrgIDAndID(ctx, orgID, id)
if err != nil {
return nil, err
}
return user, nil
return types.NewUserFromStorable(storableUser), nil
}
func (module *getter) Get(ctx context.Context, id valuer.UUID) (*types.User, error) {
user, err := module.store.GetUser(ctx, id)
storableUser, err := module.store.GetUser(ctx, id)
if err != nil {
return nil, err
}
return user, nil
return types.NewUserFromStorable(storableUser), nil
}
func (module *getter) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*types.User, error) {
users, err := module.store.ListUsersByEmailAndOrgIDs(ctx, email, orgIDs)
storableUsers, err := module.store.ListUsersByEmailAndOrgIDs(ctx, email, orgIDs)
if err != nil {
return nil, err
}
return users, nil
return types.NewUsersFromStorables(storableUsers), nil
}
func (module *getter) CountByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error) {

View File

@@ -51,7 +51,7 @@ func NewModule(store types.UserStore, tokenizer tokenizer.Tokenizer, emailing em
func (m *Module) AcceptInvite(ctx context.Context, token string, password string) (*types.User, error) {
// get the user by reset password token
user, err := m.store.GetUserByResetPasswordToken(ctx, token)
storableUser, err := m.store.GetUserByResetPasswordToken(ctx, token)
if err != nil {
return nil, err
}
@@ -63,21 +63,23 @@ func (m *Module) AcceptInvite(ctx context.Context, token string, password string
}
// query the user again
user, err = m.store.GetByOrgIDAndID(ctx, user.OrgID, user.ID)
storableUser, err = m.store.GetByOrgIDAndID(ctx, storableUser.OrgID, storableUser.ID)
if err != nil {
return nil, err
}
return user, nil
return types.NewUserFromStorable(storableUser), nil
}
func (m *Module) GetInviteByToken(ctx context.Context, token string) (*types.Invite, error) {
// get the user
user, err := m.store.GetUserByResetPasswordToken(ctx, token)
storableUser, err := m.store.GetUserByResetPasswordToken(ctx, token)
if err != nil {
return nil, err
}
user := types.NewUserFromStorable(storableUser)
// create a dummy invite obj for backward compatibility
invite := &types.Invite{
Identifiable: types.Identifiable{
@@ -109,10 +111,11 @@ func (m *Module) CreateBulkInvite(ctx context.Context, orgID valuer.UUID, userID
for idx, invite := range bulkInvites.Invites {
emails[idx] = invite.Email.StringValue()
}
users, err := m.store.GetUsersByEmailsOrgIDAndStatuses(ctx, orgID, emails, []string{types.UserStatusActive.StringValue(), types.UserStatusPendingInvite.StringValue()})
storableUsers, err := m.store.GetUsersByEmailsOrgIDAndStatuses(ctx, orgID, emails, []string{types.UserStatusActive.StringValue(), types.UserStatusPendingInvite.StringValue()})
if err != nil {
return nil, err
}
users := types.NewUsersFromStorables(storableUsers)
if len(users) > 0 {
if err := users[0].ErrIfRoot(); err != nil {
@@ -203,7 +206,7 @@ func (m *Module) CreateBulkInvite(ctx context.Context, orgID valuer.UUID, userID
resetLink := userWithToken.ResetPasswordToken.FactorPasswordResetLink(frontendBaseUrl)
tokenLifetime := m.config.Password.Invite.MaxTokenLifetime
tokenLifetime := m.config.Password.Reset.MaxTokenLifetime
humanizedTokenLifetime := strings.TrimSpace(humanize.RelTime(time.Now(), time.Now().Add(tokenLifetime), "", ""))
if err := m.emailing.SendHTML(ctx, userWithToken.User.Email.String(), "You're Invited to Join SigNoz", emailtypes.TemplateNameInvitationEmail, map[string]any{
@@ -220,12 +223,13 @@ func (m *Module) CreateBulkInvite(ctx context.Context, orgID valuer.UUID, userID
func (m *Module) ListInvite(ctx context.Context, orgID string) ([]*types.Invite, error) {
// find all the users with pending_invite status
users, err := m.store.ListUsersByOrgID(ctx, valuer.MustNewUUID(orgID))
storableUsers, err := m.store.ListUsersByOrgID(ctx, valuer.MustNewUUID(orgID))
if err != nil {
return nil, err
}
pendingUsers := slices.DeleteFunc(users, func(user *types.User) bool { return user.Status != types.UserStatusPendingInvite })
pendingStorableUsers := slices.DeleteFunc(storableUsers, func(user *types.StorableUser) bool { return user.Status != types.UserStatusPendingInvite })
pendingUsers := types.NewUsersFromStorables(pendingStorableUsers)
var invites []*types.Invite
@@ -268,7 +272,7 @@ func (module *Module) CreateUser(ctx context.Context, input *types.User, opts ..
}
if err := module.store.RunInTx(ctx, func(ctx context.Context) error {
if err := module.store.CreateUser(ctx, input); err != nil {
if err := module.store.CreateUser(ctx, types.NewStorableUser(input)); err != nil {
return err
}
@@ -291,11 +295,13 @@ func (module *Module) CreateUser(ctx context.Context, input *types.User, opts ..
}
func (m *Module) UpdateUser(ctx context.Context, orgID valuer.UUID, id string, user *types.User, updatedBy string) (*types.User, error) {
existingUser, err := m.store.GetUser(ctx, valuer.MustNewUUID(id))
existingStorableUser, err := m.store.GetUser(ctx, valuer.MustNewUUID(id))
if err != nil {
return nil, err
}
existingUser := types.NewUserFromStorable(existingStorableUser)
if err := existingUser.ErrIfRoot(); err != nil {
return nil, errors.WithAdditionalf(err, "cannot update root user")
}
@@ -350,7 +356,8 @@ func (m *Module) UpdateUser(ctx context.Context, orgID valuer.UUID, id string, u
}
func (module *Module) UpdateAnyUser(ctx context.Context, orgID valuer.UUID, user *types.User) error {
if err := module.store.UpdateUser(ctx, orgID, user); err != nil {
storableUser := types.NewStorableUser(user)
if err := module.store.UpdateUser(ctx, orgID, storableUser); err != nil {
return err
}
@@ -366,11 +373,13 @@ func (module *Module) UpdateAnyUser(ctx context.Context, orgID valuer.UUID, user
}
func (module *Module) DeleteUser(ctx context.Context, orgID valuer.UUID, id string, deletedBy string) error {
user, err := module.store.GetUser(ctx, valuer.MustNewUUID(id))
storableUser, err := module.store.GetUser(ctx, valuer.MustNewUUID(id))
if err != nil {
return err
}
user := types.NewUserFromStorable(storableUser)
if err := user.ErrIfRoot(); err != nil {
return errors.WithAdditionalf(err, "cannot delete root user")
}
@@ -412,11 +421,13 @@ func (module *Module) DeleteUser(ctx context.Context, orgID valuer.UUID, id stri
}
func (module *Module) GetOrCreateResetPasswordToken(ctx context.Context, userID valuer.UUID) (*types.ResetPasswordToken, error) {
user, err := module.store.GetUser(ctx, userID)
storableUser, err := module.store.GetUser(ctx, userID)
if err != nil {
return nil, err
}
user := types.NewUserFromStorable(storableUser)
if err := user.ErrIfRoot(); err != nil {
return nil, errors.WithAdditionalf(err, "cannot reset password for root user")
}
@@ -460,11 +471,7 @@ func (module *Module) GetOrCreateResetPasswordToken(ctx context.Context, userID
}
// create a new token
tokenLifetime := module.config.Password.Reset.MaxTokenLifetime
if user.Status == types.UserStatusPendingInvite {
tokenLifetime = module.config.Password.Invite.MaxTokenLifetime
}
resetPasswordToken, err := types.NewResetPasswordToken(password.ID, time.Now().Add(tokenLifetime))
resetPasswordToken, err := types.NewResetPasswordToken(password.ID, time.Now().Add(module.config.Password.Reset.MaxTokenLifetime))
if err != nil {
return nil, err
}
@@ -504,9 +511,6 @@ func (module *Module) ForgotPassword(ctx context.Context, orgID valuer.UUID, ema
resetLink := token.FactorPasswordResetLink(frontendBaseURL)
tokenLifetime := module.config.Password.Reset.MaxTokenLifetime
if user.Status == types.UserStatusPendingInvite {
tokenLifetime = module.config.Password.Invite.MaxTokenLifetime
}
humanizedTokenLifetime := strings.TrimSpace(humanize.RelTime(time.Now(), time.Now().Add(tokenLifetime), "", ""))
if err := module.emailing.SendHTML(
@@ -541,11 +545,13 @@ func (module *Module) UpdatePasswordByResetPasswordToken(ctx context.Context, to
return err
}
user, err := module.store.GetUser(ctx, valuer.MustNewUUID(password.UserID))
storableUser, err := module.store.GetUser(ctx, valuer.MustNewUUID(password.UserID))
if err != nil {
return err
}
user := types.NewUserFromStorable(storableUser)
// handle deleted user
if err := user.ErrIfDeleted(); err != nil {
return errors.WithAdditionalf(err, "deleted users cannot reset their password")
@@ -576,7 +582,7 @@ func (module *Module) UpdatePasswordByResetPasswordToken(ctx context.Context, to
if err := user.UpdateStatus(types.UserStatusActive); err != nil {
return err
}
if err := module.store.UpdateUser(ctx, user.OrgID, user); err != nil {
if err := module.store.UpdateUser(ctx, user.OrgID, types.NewStorableUser(user)); err != nil {
return err
}
}
@@ -594,11 +600,13 @@ func (module *Module) UpdatePasswordByResetPasswordToken(ctx context.Context, to
}
func (module *Module) UpdatePassword(ctx context.Context, userID valuer.UUID, oldpasswd string, passwd string) error {
user, err := module.store.GetUser(ctx, userID)
storableUser, err := module.store.GetUser(ctx, userID)
if err != nil {
return err
}
user := types.NewUserFromStorable(storableUser)
if err := user.ErrIfDeleted(); err != nil {
return errors.WithAdditionalf(err, "cannot change password for deleted user")
}
@@ -750,11 +758,13 @@ func (module *Module) Collect(ctx context.Context, orgID valuer.UUID) (map[strin
// this function restricts that only one non-deleted user email can exist for an org ID, if found more, it throws an error
func (module *Module) GetNonDeletedUserByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) (*types.User, error) {
existingUsers, err := module.store.GetUsersByEmailAndOrgID(ctx, email, orgID)
existingStorableUsers, err := module.store.GetUsersByEmailAndOrgID(ctx, email, orgID)
if err != nil {
return nil, err
}
existingUsers := types.NewUsersFromStorables(existingStorableUsers)
// filter out the deleted users
existingUsers = slices.DeleteFunc(existingUsers, func(user *types.User) bool { return user.ErrIfDeleted() != nil })
@@ -773,7 +783,7 @@ func (module *Module) GetNonDeletedUserByEmailAndOrgID(ctx context.Context, emai
func (module *Module) createUserWithoutGrant(ctx context.Context, input *types.User, opts ...root.CreateUserOption) error {
createUserOpts := root.NewCreateUserOptions(opts...)
if err := module.store.RunInTx(ctx, func(ctx context.Context) error {
if err := module.store.CreateUser(ctx, input); err != nil {
if err := module.store.CreateUser(ctx, types.NewStorableUser(input)); err != nil {
return err
}
@@ -809,7 +819,7 @@ func (module *Module) activatePendingUser(ctx context.Context, user *types.User)
if err := user.UpdateStatus(types.UserStatusActive); err != nil {
return err
}
err = module.store.UpdateUser(ctx, user.OrgID, user)
err = module.store.UpdateUser(ctx, user.OrgID, types.NewStorableUser(user))
if err != nil {
return err
}

View File

@@ -129,10 +129,11 @@ func (s *service) reconcileByName(ctx context.Context) error {
}
func (s *service) reconcileRootUser(ctx context.Context, orgID valuer.UUID) error {
existingRoot, err := s.store.GetRootUserByOrgID(ctx, orgID)
existingStorableRoot, err := s.store.GetRootUserByOrgID(ctx, orgID)
if err != nil && !errors.Ast(err, errors.TypeNotFound) {
return err
}
existingRoot := types.NewUserFromStorable(existingStorableRoot)
if existingRoot == nil {
return s.createOrPromoteRootUser(ctx, orgID)

View File

@@ -39,7 +39,7 @@ func (store *store) CreatePassword(ctx context.Context, password *types.FactorPa
return nil
}
func (store *store) CreateUser(ctx context.Context, user *types.User) error {
func (store *store) CreateUser(ctx context.Context, user *types.StorableUser) error {
_, err := store.
sqlstore.
BunDBCtx(ctx).
@@ -52,8 +52,8 @@ func (store *store) CreateUser(ctx context.Context, user *types.User) error {
return nil
}
func (store *store) GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*types.User, error) {
var users []*types.User
func (store *store) GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*types.StorableUser, error) {
var users []*types.StorableUser
err := store.
sqlstore.
@@ -69,8 +69,8 @@ func (store *store) GetUsersByEmail(ctx context.Context, email valuer.Email) ([]
return users, nil
}
func (store *store) GetUser(ctx context.Context, id valuer.UUID) (*types.User, error) {
user := new(types.User)
func (store *store) GetUser(ctx context.Context, id valuer.UUID) (*types.StorableUser, error) {
user := new(types.StorableUser)
err := store.
sqlstore.
@@ -86,8 +86,8 @@ func (store *store) GetUser(ctx context.Context, id valuer.UUID) (*types.User, e
return user, nil
}
func (store *store) GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*types.User, error) {
user := new(types.User)
func (store *store) GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*types.StorableUser, error) {
user := new(types.StorableUser)
err := store.
sqlstore.
@@ -104,8 +104,8 @@ func (store *store) GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id v
return user, nil
}
func (store *store) GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) ([]*types.User, error) {
var users []*types.User
func (store *store) GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) ([]*types.StorableUser, error) {
var users []*types.StorableUser
err := store.
sqlstore.
@@ -122,8 +122,8 @@ func (store *store) GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Em
return users, nil
}
func (store *store) GetActiveUsersByRoleAndOrgID(ctx context.Context, role types.Role, orgID valuer.UUID) ([]*types.User, error) {
var users []*types.User
func (store *store) GetActiveUsersByRoleAndOrgID(ctx context.Context, role types.Role, orgID valuer.UUID) ([]*types.StorableUser, error) {
var users []*types.StorableUser
err := store.
sqlstore.
@@ -141,7 +141,7 @@ func (store *store) GetActiveUsersByRoleAndOrgID(ctx context.Context, role types
return users, nil
}
func (store *store) UpdateUser(ctx context.Context, orgID valuer.UUID, user *types.User) error {
func (store *store) UpdateUser(ctx context.Context, orgID valuer.UUID, user *types.StorableUser) error {
_, err := store.
sqlstore.
BunDBCtx(ctx).
@@ -162,8 +162,8 @@ func (store *store) UpdateUser(ctx context.Context, orgID valuer.UUID, user *typ
return nil
}
func (store *store) ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*types.GettableUser, error) {
users := []*types.User{}
func (store *store) ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*types.StorableUser, error) {
users := []*types.StorableUser{}
err := store.
sqlstore.
@@ -247,7 +247,7 @@ func (store *store) DeleteUser(ctx context.Context, orgID string, id string) err
// delete user
_, err = tx.NewDelete().
Model(new(types.User)).
Model(new(types.StorableUser)).
Where("org_id = ?", orgID).
Where("id = ?", id).
Exec(ctx)
@@ -332,7 +332,7 @@ func (store *store) SoftDeleteUser(ctx context.Context, orgID string, id string)
// soft delete user
now := time.Now()
_, err = tx.NewUpdate().
Model(new(types.User)).
Model(new(types.StorableUser)).
Set("status = ?", types.UserStatusDeleted).
Set("deleted_at = ?", now).
Set("updated_at = ?", now).
@@ -580,7 +580,7 @@ func (store *store) CountByOrgID(ctx context.Context, orgID valuer.UUID) (int64,
}
func (store *store) CountByOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, statuses []string) (map[valuer.String]int64, error) {
user := new(types.User)
user := new(types.StorableUser)
var results []struct {
Status valuer.String `bun:"status"`
Count int64 `bun:"count"`
@@ -633,8 +633,8 @@ func (store *store) RunInTx(ctx context.Context, cb func(ctx context.Context) er
})
}
func (store *store) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*types.User, error) {
user := new(types.User)
func (store *store) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*types.StorableUser, error) {
user := new(types.StorableUser)
err := store.
sqlstore.
BunDBCtx(ctx).
@@ -649,8 +649,8 @@ func (store *store) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (
return user, nil
}
func (store *store) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*types.User, error) {
users := []*types.User{}
func (store *store) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*types.StorableUser, error) {
users := []*types.StorableUser{}
err := store.
sqlstore.
BunDB().
@@ -666,8 +666,8 @@ func (store *store) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.
return users, nil
}
func (store *store) GetUserByResetPasswordToken(ctx context.Context, token string) (*types.User, error) {
user := new(types.User)
func (store *store) GetUserByResetPasswordToken(ctx context.Context, token string) (*types.StorableUser, error) {
user := new(types.StorableUser)
err := store.
sqlstore.
@@ -685,8 +685,8 @@ func (store *store) GetUserByResetPasswordToken(ctx context.Context, token strin
return user, nil
}
func (store *store) GetUsersByEmailsOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, emails []string, statuses []string) ([]*types.User, error) {
users := []*types.User{}
func (store *store) GetUsersByEmailsOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, emails []string, statuses []string) ([]*types.StorableUser, error) {
users := []*types.StorableUser{}
err := store.
sqlstore.

View File

@@ -2,6 +2,7 @@ package sqlmigration
import (
"context"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types"
@@ -16,12 +17,12 @@ type funnel struct {
types.Identifiable // funnel id
types.TimeAuditable
types.UserAuditable
Name string `json:"funnel_name" bun:"name,type:text,notnull"` // funnel name
Description string `json:"description" bun:"description,type:text"` // funnel description
OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"`
Steps []funnelStep `json:"steps" bun:"steps,type:text,notnull"`
Tags string `json:"tags" bun:"tags,type:text"`
CreatedByUser *types.User `json:"user" bun:"rel:belongs-to,join:created_by=id"`
Name string `json:"funnel_name" bun:"name,type:text,notnull"` // funnel name
Description string `json:"description" bun:"description,type:text"` // funnel description
OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"`
Steps []funnelStep `json:"steps" bun:"steps,type:text,notnull"`
Tags string `json:"tags" bun:"tags,type:text"`
CreatedByUser *types.StorableUser `json:"user" bun:"rel:belongs-to,join:created_by=id"`
}
type funnelStep struct {

View File

@@ -34,7 +34,7 @@ func (store *store) Create(ctx context.Context, token *authtypes.StorableToken)
}
func (store *store) GetIdentityByUserID(ctx context.Context, userID valuer.UUID) (*authtypes.Identity, error) {
user := new(types.User)
user := new(types.StorableUser)
err := store.
sqlstore.

View File

@@ -128,7 +128,7 @@ func (typ *Identity) ToClaims() Claims {
type AuthNStore interface {
// Get user and factor password by email and orgID.
GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx context.Context, email string, orgID valuer.UUID) (*types.User, *types.FactorPassword, error)
GetActiveUserAndFactorPasswordByEmailAndOrgID(ctx context.Context, email string, orgID valuer.UUID) (*types.StorableUser, *types.FactorPassword, error)
// Get org domain from id.
GetAuthDomainFromID(ctx context.Context, domainID valuer.UUID) (*AuthDomain, error)

View File

@@ -39,15 +39,15 @@ type OrgUserAPIKey struct {
}
type UserWithAPIKey struct {
*User `bun:",extend"`
APIKeys []*StorableAPIKeyUser `bun:"rel:has-many,join:id=user_id"`
*StorableUser `bun:",extend"`
APIKeys []*StorableAPIKeyUser `bun:"rel:has-many,join:id=user_id"`
}
type StorableAPIKeyUser struct {
StorableAPIKey `bun:",extend"`
CreatedByUser *User `json:"createdByUser" bun:"created_by_user,rel:belongs-to,join:created_by=id"`
UpdatedByUser *User `json:"updatedByUser" bun:"updated_by_user,rel:belongs-to,join:updated_by=id"`
CreatedByUser *StorableUser `json:"createdByUser" bun:"created_by_user,rel:belongs-to,join:created_by=id"`
UpdatedByUser *StorableUser `json:"updatedByUser" bun:"updated_by_user,rel:belongs-to,join:updated_by=id"`
}
type StorableAPIKey struct {
@@ -138,7 +138,7 @@ func NewGettableAPIKeyFromStorableAPIKey(storableAPIKey *StorableAPIKeyUser) *Ge
LastUsed: lastUsed,
Revoked: storableAPIKey.Revoked,
UserID: storableAPIKey.UserID.String(),
CreatedByUser: storableAPIKey.CreatedByUser,
UpdatedByUser: storableAPIKey.UpdatedByUser,
CreatedByUser: NewUserFromStorable(storableAPIKey.CreatedByUser),
UpdatedByUser: NewUserFromStorable(storableAPIKey.UpdatedByUser),
}
}

View File

@@ -18,12 +18,12 @@ type StorableFunnel struct {
types.TimeAuditable
types.UserAuditable
bun.BaseModel `bun:"table:trace_funnel"`
Name string `json:"funnel_name" bun:"name,type:text,notnull"`
Description string `json:"description" bun:"description,type:text"`
OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"`
Steps []*FunnelStep `json:"steps" bun:"steps,type:text,notnull"`
Tags string `json:"tags" bun:"tags,type:text"`
CreatedByUser *types.User `json:"user" bun:"rel:belongs-to,join:created_by=id"`
Name string `json:"funnel_name" bun:"name,type:text,notnull"`
Description string `json:"description" bun:"description,type:text"`
OrgID valuer.UUID `json:"org_id" bun:"org_id,type:varchar,notnull"`
Steps []*FunnelStep `json:"steps" bun:"steps,type:text,notnull"`
Tags string `json:"tags" bun:"tags,type:text"`
CreatedByUser *types.StorableUser `json:"user" bun:"rel:belongs-to,join:created_by=id"`
}
type FunnelStep struct {

View File

@@ -443,7 +443,7 @@ func TestConstructFunnelResponse(t *testing.T) {
},
Name: "test-funnel",
OrgID: orgID,
CreatedByUser: &types.User{
CreatedByUser: &types.StorableUser{
Identifiable: types.Identifiable{
ID: userID,
},

View File

@@ -33,15 +33,25 @@ var (
ValidUserStatus = []valuer.String{UserStatusPendingInvite, UserStatusActive, UserStatusDeleted}
)
type GettableUser = User
type User struct {
Identifiable
DisplayName string `json:"displayName"`
Email valuer.Email `json:"email"`
Role Role `json:"role"` // this will be moved to roles
OrgID valuer.UUID `json:"orgId"`
IsRoot bool `json:"isRoot"`
Status valuer.String `json:"status"`
DeletedAt time.Time `json:"-"`
TimeAuditable
}
type StorableUser struct {
bun.BaseModel `bun:"table:users"`
Identifiable
DisplayName string `bun:"display_name" json:"displayName"`
Email valuer.Email `bun:"email" json:"email"`
Role Role `bun:"role" json:"role"`
Role Role `bun:"role" json:"role"` // this will be removed as column from here
OrgID valuer.UUID `bun:"org_id" json:"orgId"`
IsRoot bool `bun:"is_root" json:"isRoot"`
Status valuer.String `bun:"status" json:"status"`
@@ -57,6 +67,57 @@ type PostableRegisterOrgAndAdmin struct {
OrgName string `json:"orgName"`
}
func NewStorableUser(user *User) *StorableUser {
if user == nil {
return nil
}
return &StorableUser{
Identifiable: user.Identifiable,
DisplayName: user.DisplayName,
Email: user.Email,
Role: user.Role,
OrgID: user.OrgID,
IsRoot: user.IsRoot,
Status: user.Status,
DeletedAt: user.DeletedAt,
TimeAuditable: user.TimeAuditable,
}
}
func NewUserFromStorable(storableUser *StorableUser) *User {
if storableUser == nil {
return nil
}
return &User{
Identifiable: storableUser.Identifiable,
DisplayName: storableUser.DisplayName,
Email: storableUser.Email,
Role: storableUser.Role,
OrgID: storableUser.OrgID,
IsRoot: storableUser.IsRoot,
Status: storableUser.Status,
DeletedAt: storableUser.DeletedAt,
TimeAuditable: storableUser.TimeAuditable,
}
}
func NewUsersFromStorables(storableUsers []*StorableUser) []*User {
users := make([]*User, len(storableUsers))
for i, s := range storableUsers {
users[i] = NewUserFromStorable(s)
}
return users
}
func NewStorableUsers(users []*User) []*StorableUser {
storableUsers := make([]*StorableUser, len(users))
for i, u := range users {
storableUsers[i] = NewStorableUser(u)
}
return storableUsers
}
func NewUser(displayName string, email valuer.Email, role Role, orgID valuer.UUID, status valuer.String) (*User, error) {
if email.IsZero() {
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "email is required")
@@ -215,33 +276,33 @@ func (request *PostableRegisterOrgAndAdmin) UnmarshalJSON(data []byte) error {
type UserStore interface {
// Creates a user.
CreateUser(ctx context.Context, user *User) error
CreateUser(ctx context.Context, user *StorableUser) error
// Get user by id.
GetUser(context.Context, valuer.UUID) (*User, error)
GetUser(context.Context, valuer.UUID) (*StorableUser, error)
// Get user by orgID and id.
GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*User, error)
GetByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*StorableUser, error)
// Get user by email and orgID.
GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) ([]*User, error)
GetUsersByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) ([]*StorableUser, error)
// Get users by email.
GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*User, error)
GetUsersByEmail(ctx context.Context, email valuer.Email) ([]*StorableUser, error)
// Get users by role and org.
GetActiveUsersByRoleAndOrgID(ctx context.Context, role Role, orgID valuer.UUID) ([]*User, error)
GetActiveUsersByRoleAndOrgID(ctx context.Context, role Role, orgID valuer.UUID) ([]*StorableUser, error)
// List users by org.
ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*User, error)
ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*StorableUser, error)
// List users by email and org ids.
ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*User, error)
ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*StorableUser, error)
// Get users for an org id using emails and statuses
GetUsersByEmailsOrgIDAndStatuses(context.Context, valuer.UUID, []string, []string) ([]*User, error)
GetUsersByEmailsOrgIDAndStatuses(context.Context, valuer.UUID, []string, []string) ([]*StorableUser, error)
UpdateUser(ctx context.Context, orgID valuer.UUID, user *User) error
UpdateUser(ctx context.Context, orgID valuer.UUID, user *StorableUser) error
DeleteUser(ctx context.Context, orgID string, id string) error
SoftDeleteUser(ctx context.Context, orgID string, id string) error
@@ -267,10 +328,10 @@ type UserStore interface {
CountByOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, statuses []string) (map[valuer.String]int64, error)
// Get root user by org.
GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*User, error)
GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*StorableUser, error)
// Get user by reset password token
GetUserByResetPasswordToken(ctx context.Context, token string) (*User, error)
GetUserByResetPasswordToken(ctx context.Context, token string) (*StorableUser, error)
// Transaction
RunInTx(ctx context.Context, cb func(ctx context.Context) error) error