mirror of
https://github.com/SigNoz/signoz.git
synced 2026-08-12 16:00:46 +01:00
Compare commits
10 Commits
issue_5602
...
chore/remo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5ccbe7558 | ||
|
|
2616885d22 | ||
|
|
52dd57074e | ||
|
|
fe94b817db | ||
|
|
ea36032d96 | ||
|
|
6ecfa839f3 | ||
|
|
62d382b3cc | ||
|
|
cc07e2fa24 | ||
|
|
d7aa63f1bc | ||
|
|
c36b748370 |
@@ -1499,6 +1499,7 @@ components:
|
||||
- computeengine
|
||||
- gke
|
||||
- cloudstorage
|
||||
- cloudsql_mysql
|
||||
type: string
|
||||
CloudintegrationtypesServiceMetadata:
|
||||
properties:
|
||||
@@ -8865,31 +8866,6 @@ components:
|
||||
oldPassword:
|
||||
type: string
|
||||
type: object
|
||||
TypesDeprecatedUser:
|
||||
properties:
|
||||
createdAt:
|
||||
format: date-time
|
||||
type: string
|
||||
displayName:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
isRoot:
|
||||
type: boolean
|
||||
orgId:
|
||||
type: string
|
||||
role:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
updatedAt:
|
||||
format: date-time
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
TypesIdentifiable:
|
||||
properties:
|
||||
id:
|
||||
@@ -8897,31 +8873,6 @@ components:
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
TypesInvite:
|
||||
properties:
|
||||
createdAt:
|
||||
format: date-time
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
inviteLink:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
orgId:
|
||||
type: string
|
||||
role:
|
||||
type: string
|
||||
token:
|
||||
type: string
|
||||
updatedAt:
|
||||
format: date-time
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
TypesOrganization:
|
||||
properties:
|
||||
alias:
|
||||
@@ -8956,17 +8907,6 @@ components:
|
||||
- orgId
|
||||
- email
|
||||
type: object
|
||||
TypesPostableInvite:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
frontendBaseUrl:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
role:
|
||||
type: string
|
||||
type: object
|
||||
TypesPostableResetPassword:
|
||||
properties:
|
||||
password:
|
||||
@@ -8974,13 +8914,6 @@ components:
|
||||
token:
|
||||
type: string
|
||||
type: object
|
||||
TypesPostableRole:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
TypesPostableVerifyResetPasswordToken:
|
||||
properties:
|
||||
token:
|
||||
@@ -11395,70 +11328,6 @@ paths:
|
||||
summary: Get field values
|
||||
tags:
|
||||
- fields
|
||||
/api/v1/getResetPasswordToken/{id}:
|
||||
get:
|
||||
deprecated: true
|
||||
description: This endpoint returns the reset password token by id
|
||||
operationId: GetResetPasswordTokenDeprecated
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/TypesResetPasswordToken'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"404":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Not Found
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: Get reset password token
|
||||
tags:
|
||||
- users
|
||||
/api/v1/global/config:
|
||||
get:
|
||||
deprecated: false
|
||||
@@ -11488,69 +11357,6 @@ paths:
|
||||
summary: Get global config
|
||||
tags:
|
||||
- global
|
||||
/api/v1/invite:
|
||||
post:
|
||||
deprecated: true
|
||||
description: This endpoint creates an invite for a user
|
||||
operationId: CreateInvite
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TypesPostableInvite'
|
||||
responses:
|
||||
"201":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/TypesInvite'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: Created
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"409":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Conflict
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: Create invite
|
||||
tags:
|
||||
- users
|
||||
/api/v1/llm_pricing_rules:
|
||||
get:
|
||||
deprecated: false
|
||||
@@ -12202,40 +12008,6 @@ paths:
|
||||
summary: Get query range result
|
||||
tags:
|
||||
- dashboard
|
||||
/api/v1/resetPassword:
|
||||
post:
|
||||
deprecated: true
|
||||
description: This endpoint resets the password by token
|
||||
operationId: ResetPasswordDeprecated
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TypesPostableResetPassword'
|
||||
responses:
|
||||
"204":
|
||||
description: No Content
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"409":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Conflict
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
summary: Reset password
|
||||
tags:
|
||||
- users
|
||||
/api/v1/roles:
|
||||
get:
|
||||
deprecated: false
|
||||
@@ -14426,97 +14198,6 @@ paths:
|
||||
summary: Get aggregations for a trace
|
||||
tags:
|
||||
- tracedetail
|
||||
/api/v1/user:
|
||||
get:
|
||||
deprecated: true
|
||||
description: This endpoint lists all users
|
||||
operationId: ListUsersDeprecated
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/components/schemas/TypesDeprecatedUser'
|
||||
type: array
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: List users
|
||||
tags:
|
||||
- users
|
||||
/api/v1/user/me:
|
||||
get:
|
||||
deprecated: true
|
||||
description: This endpoint returns the user I belong to
|
||||
operationId: GetMyUserDeprecated
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/TypesDeprecatedUser'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- tokenizer: []
|
||||
summary: Get my user
|
||||
tags:
|
||||
- users
|
||||
/api/v1/user/preferences:
|
||||
get:
|
||||
deprecated: false
|
||||
@@ -23761,108 +23442,6 @@ paths:
|
||||
summary: Get user roles
|
||||
tags:
|
||||
- users
|
||||
post:
|
||||
deprecated: true
|
||||
description: This endpoint assigns the role to the user roles by user id
|
||||
operationId: SetRoleByUserID
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TypesPostableRole'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"404":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Not Found
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: Set user roles
|
||||
tags:
|
||||
- users
|
||||
/api/v2/users/{id}/roles/{roleId}:
|
||||
delete:
|
||||
deprecated: true
|
||||
description: This endpoint removes a role from the user by user id and role
|
||||
id
|
||||
operationId: RemoveUserRoleByUserIDAndRoleID
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: roleId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"204":
|
||||
description: No Content
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"404":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Not Found
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: Remove a role from user
|
||||
tags:
|
||||
- users
|
||||
/api/v2/users/me:
|
||||
get:
|
||||
deprecated: false
|
||||
|
||||
@@ -388,6 +388,10 @@ function App(): JSX.Element {
|
||||
if (error?.name === 'AbortError') {
|
||||
return null;
|
||||
}
|
||||
// Ignore benign Monaco cancellation errors (name 'Canceled').
|
||||
if (error?.name === 'Canceled') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Drop the event if its level is 'warning' or 'info'
|
||||
if (event.level === 'warning' || event.level === 'info') {
|
||||
|
||||
@@ -2818,6 +2818,7 @@ export enum CloudintegrationtypesServiceIDDTO {
|
||||
computeengine = 'computeengine',
|
||||
gke = 'gke',
|
||||
cloudstorage = 'cloudstorage',
|
||||
cloudsql_mysql = 'cloudsql_mysql',
|
||||
}
|
||||
export type CloudintegrationtypesCloudIntegrationServiceDTOAnyOf = {
|
||||
/**
|
||||
@@ -9933,47 +9934,6 @@ export interface TypesChangePasswordRequestDTO {
|
||||
oldPassword?: string;
|
||||
}
|
||||
|
||||
export interface TypesDeprecatedUserDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
createdAt?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
isRoot?: boolean;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
orgId?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
role?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
export interface TypesIdentifiableDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -9981,47 +9941,6 @@ export interface TypesIdentifiableDTO {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface TypesInviteDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
createdAt?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
inviteLink?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
orgId?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
role?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
token?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
export interface TypesOrganizationDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -10071,25 +9990,6 @@ export interface TypesPostableForgotPasswordDTO {
|
||||
orgId: string;
|
||||
}
|
||||
|
||||
export interface TypesPostableInviteDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
frontendBaseUrl?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
role?: string;
|
||||
}
|
||||
|
||||
export interface TypesPostableResetPasswordDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -10101,13 +10001,6 @@ export interface TypesPostableResetPasswordDTO {
|
||||
token?: string;
|
||||
}
|
||||
|
||||
export interface TypesPostableRoleDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface TypesPostableVerifyResetPasswordTokenDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -10750,17 +10643,6 @@ export type GetFieldsValues200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetResetPasswordTokenDeprecatedPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetResetPasswordTokenDeprecated200 = {
|
||||
data: TypesResetPasswordTokenDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetGlobalConfig200 = {
|
||||
data: GlobaltypesConfigDTO;
|
||||
/**
|
||||
@@ -10769,14 +10651,6 @@ export type GetGlobalConfig200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type CreateInvite201 = {
|
||||
data: TypesInviteDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListLLMPricingRulesParams = {
|
||||
/**
|
||||
* @type integer
|
||||
@@ -11189,25 +11063,6 @@ export type GetTraceAggregations200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListUsersDeprecated200 = {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
data: TypesDeprecatedUserDTO[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetMyUserDeprecated200 = {
|
||||
data: TypesDeprecatedUserDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListUserPreferences200 = {
|
||||
/**
|
||||
* @type array
|
||||
@@ -12402,13 +12257,6 @@ export type GetRolesByUserID200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type SetRoleByUserIDPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type RemoveUserRoleByUserIDAndRoleIDPathParameters = {
|
||||
id: string;
|
||||
roleId: string;
|
||||
};
|
||||
export type GetMyUser200 = {
|
||||
data: AuthtypesUserWithRolesDTO;
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,6 @@ import type {
|
||||
import type {
|
||||
AuthtypesPostableUserDTO,
|
||||
AuthtypesPostableUserRoleDTO,
|
||||
CreateInvite201,
|
||||
CreateResetPasswordToken201,
|
||||
CreateResetPasswordTokenPathParameters,
|
||||
CreateUser201,
|
||||
@@ -28,10 +27,7 @@ import type {
|
||||
DeleteUserPathParameters,
|
||||
DeleteUserRolePathParameters,
|
||||
GetMyUser200,
|
||||
GetMyUserDeprecated200,
|
||||
GetResetPasswordToken200,
|
||||
GetResetPasswordTokenDeprecated200,
|
||||
GetResetPasswordTokenDeprecatedPathParameters,
|
||||
GetResetPasswordTokenPathParameters,
|
||||
GetRolesByUserID200,
|
||||
GetRolesByUserIDPathParameters,
|
||||
@@ -42,15 +38,10 @@ import type {
|
||||
GetUsersByRoleID200,
|
||||
GetUsersByRoleIDPathParameters,
|
||||
ListUsers200,
|
||||
ListUsersDeprecated200,
|
||||
RemoveUserRoleByUserIDAndRoleIDPathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
SetRoleByUserIDPathParameters,
|
||||
TypesChangePasswordRequestDTO,
|
||||
TypesPostableForgotPasswordDTO,
|
||||
TypesPostableInviteDTO,
|
||||
TypesPostableResetPasswordDTO,
|
||||
TypesPostableRoleDTO,
|
||||
TypesPostableVerifyResetPasswordTokenDTO,
|
||||
TypesUpdatableUserDTO,
|
||||
UpdateUserPathParameters,
|
||||
@@ -59,460 +50,6 @@ import type {
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
/**
|
||||
* This endpoint returns the reset password token by id
|
||||
* @deprecated
|
||||
* @summary Get reset password token
|
||||
*/
|
||||
export const getResetPasswordTokenDeprecated = (
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetResetPasswordTokenDeprecated200>({
|
||||
url: `/api/v1/getResetPasswordToken/${id}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetResetPasswordTokenDeprecatedQueryKey = ({
|
||||
id,
|
||||
}: GetResetPasswordTokenDeprecatedPathParameters) => {
|
||||
return [`/api/v1/getResetPasswordToken/${id}`] as const;
|
||||
};
|
||||
|
||||
export const getGetResetPasswordTokenDeprecatedQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetResetPasswordTokenDeprecatedQueryKey({ id });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>
|
||||
> = ({ signal }) => getResetPasswordTokenDeprecated({ id }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetResetPasswordTokenDeprecatedQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>
|
||||
>;
|
||||
export type GetResetPasswordTokenDeprecatedQueryError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Get reset password token
|
||||
*/
|
||||
|
||||
export function useGetResetPasswordTokenDeprecated<
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetResetPasswordTokenDeprecatedQueryOptions(
|
||||
{ id },
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Get reset password token
|
||||
*/
|
||||
export const invalidateGetResetPasswordTokenDeprecated = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetResetPasswordTokenDeprecatedQueryKey({ id }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint creates an invite for a user
|
||||
* @deprecated
|
||||
* @summary Create invite
|
||||
*/
|
||||
export const createInvite = (
|
||||
typesPostableInviteDTO?: BodyType<TypesPostableInviteDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateInvite201>({
|
||||
url: `/api/v1/invite`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: typesPostableInviteDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCreateInviteMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createInvite>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createInvite>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createInvite'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof createInvite>>,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return createInvite(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type CreateInviteMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createInvite>>
|
||||
>;
|
||||
export type CreateInviteMutationBody =
|
||||
| BodyType<TypesPostableInviteDTO>
|
||||
| undefined;
|
||||
export type CreateInviteMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Create invite
|
||||
*/
|
||||
export const useCreateInvite = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createInvite>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createInvite>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableInviteDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getCreateInviteMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint resets the password by token
|
||||
* @deprecated
|
||||
* @summary Reset password
|
||||
*/
|
||||
export const resetPasswordDeprecated = (
|
||||
typesPostableResetPasswordDTO?: BodyType<TypesPostableResetPasswordDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v1/resetPassword`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: typesPostableResetPasswordDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getResetPasswordDeprecatedMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['resetPasswordDeprecated'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return resetPasswordDeprecated(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type ResetPasswordDeprecatedMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>
|
||||
>;
|
||||
export type ResetPasswordDeprecatedMutationBody =
|
||||
| BodyType<TypesPostableResetPasswordDTO>
|
||||
| undefined;
|
||||
export type ResetPasswordDeprecatedMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Reset password
|
||||
*/
|
||||
export const useResetPasswordDeprecated = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof resetPasswordDeprecated>>,
|
||||
TError,
|
||||
{ data?: BodyType<TypesPostableResetPasswordDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getResetPasswordDeprecatedMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint lists all users
|
||||
* @deprecated
|
||||
* @summary List users
|
||||
*/
|
||||
export const listUsersDeprecated = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<ListUsersDeprecated200>({
|
||||
url: `/api/v1/user`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListUsersDeprecatedQueryKey = () => {
|
||||
return [`/api/v1/user`] as const;
|
||||
};
|
||||
|
||||
export const getListUsersDeprecatedQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getListUsersDeprecatedQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>
|
||||
> = ({ signal }) => listUsersDeprecated(signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type ListUsersDeprecatedQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>
|
||||
>;
|
||||
export type ListUsersDeprecatedQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary List users
|
||||
*/
|
||||
|
||||
export function useListUsersDeprecated<
|
||||
TData = Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listUsersDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getListUsersDeprecatedQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary List users
|
||||
*/
|
||||
export const invalidateListUsersDeprecated = async (
|
||||
queryClient: QueryClient,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getListUsersDeprecatedQueryKey() },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the user I belong to
|
||||
* @deprecated
|
||||
* @summary Get my user
|
||||
*/
|
||||
export const getMyUserDeprecated = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<GetMyUserDeprecated200>({
|
||||
url: `/api/v1/user/me`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetMyUserDeprecatedQueryKey = () => {
|
||||
return [`/api/v1/user/me`] as const;
|
||||
};
|
||||
|
||||
export const getGetMyUserDeprecatedQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getMyUserDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getMyUserDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getGetMyUserDeprecatedQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getMyUserDeprecated>>
|
||||
> = ({ signal }) => getMyUserDeprecated(signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getMyUserDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetMyUserDeprecatedQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getMyUserDeprecated>>
|
||||
>;
|
||||
export type GetMyUserDeprecatedQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Get my user
|
||||
*/
|
||||
|
||||
export function useGetMyUserDeprecated<
|
||||
TData = Awaited<ReturnType<typeof getMyUserDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getMyUserDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetMyUserDeprecatedQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Get my user
|
||||
*/
|
||||
export const invalidateGetMyUserDeprecated = async (
|
||||
queryClient: QueryClient,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetMyUserDeprecatedQueryKey() },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint initiates the forgot password flow by sending a reset password email
|
||||
* @summary Forgot password
|
||||
@@ -1834,189 +1371,6 @@ export const invalidateGetRolesByUserID = async (
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint assigns the role to the user roles by user id
|
||||
* @deprecated
|
||||
* @summary Set user roles
|
||||
*/
|
||||
export const setRoleByUserID = (
|
||||
{ id }: SetRoleByUserIDPathParameters,
|
||||
typesPostableRoleDTO?: BodyType<TypesPostableRoleDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/users/${id}/roles`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: typesPostableRoleDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getSetRoleByUserIDMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['setRoleByUserID'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return setRoleByUserID(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type SetRoleByUserIDMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>
|
||||
>;
|
||||
export type SetRoleByUserIDMutationBody =
|
||||
| BodyType<TypesPostableRoleDTO>
|
||||
| undefined;
|
||||
export type SetRoleByUserIDMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Set user roles
|
||||
*/
|
||||
export const useSetRoleByUserID = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof setRoleByUserID>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: SetRoleByUserIDPathParameters;
|
||||
data?: BodyType<TypesPostableRoleDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getSetRoleByUserIDMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint removes a role from the user by user id and role id
|
||||
* @deprecated
|
||||
* @summary Remove a role from user
|
||||
*/
|
||||
export const removeUserRoleByUserIDAndRoleID = (
|
||||
{ id, roleId }: RemoveUserRoleByUserIDAndRoleIDPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/users/${id}/roles/${roleId}`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getRemoveUserRoleByUserIDAndRoleIDMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>,
|
||||
TError,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>,
|
||||
TError,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['removeUserRoleByUserIDAndRoleID'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return removeUserRoleByUserIDAndRoleID(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type RemoveUserRoleByUserIDAndRoleIDMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>
|
||||
>;
|
||||
|
||||
export type RemoveUserRoleByUserIDAndRoleIDMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Remove a role from user
|
||||
*/
|
||||
export const useRemoveUserRoleByUserIDAndRoleID = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>,
|
||||
TError,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof removeUserRoleByUserIDAndRoleID>>,
|
||||
TError,
|
||||
{ pathParams: RemoveUserRoleByUserIDAndRoleIDPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getRemoveUserRoleByUserIDAndRoleIDMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint returns the user I belong to
|
||||
* @summary Get my user v2
|
||||
|
||||
@@ -437,6 +437,17 @@ describe('Create Alert Channel', () => {
|
||||
render(<CreateAlertChannels preType={ChannelType.GoogleChat} />);
|
||||
});
|
||||
|
||||
// paste instead of type: a per-keystroke re-render of the whole form
|
||||
// pushes these tests past the 5s jest timeout on slower CI runners
|
||||
async function fillField(
|
||||
user: ReturnType<typeof userEvent.setup>,
|
||||
testId: string,
|
||||
value: string,
|
||||
): Promise<void> {
|
||||
await user.click(screen.getByTestId(testId));
|
||||
await user.paste(value);
|
||||
}
|
||||
|
||||
it('Should check if the selected item in the type dropdown has text "Google Chat"', () => {
|
||||
expect(screen.getByText('Google Chat')).toBeInTheDocument();
|
||||
});
|
||||
@@ -463,14 +474,8 @@ describe('Create Alert Channel', () => {
|
||||
it('Should check if saving with a webhook url outside chat.googleapis.com displays error notification', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.type(
|
||||
screen.getByTestId('channel-name-textbox'),
|
||||
'gchat-channel',
|
||||
);
|
||||
await user.type(
|
||||
screen.getByTestId('webhook-url-textbox'),
|
||||
'https://example.com/webhook',
|
||||
);
|
||||
await fillField(user, 'channel-name-textbox', 'gchat-channel');
|
||||
await fillField(user, 'webhook-url-textbox', 'https://example.com/webhook');
|
||||
|
||||
await user.click(screen.getByTestId('save-channel-button'));
|
||||
|
||||
@@ -496,11 +501,8 @@ describe('Create Alert Channel', () => {
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.type(
|
||||
screen.getByTestId('channel-name-textbox'),
|
||||
'gchat-channel',
|
||||
);
|
||||
await user.type(screen.getByTestId('webhook-url-textbox'), validWebhookUrl);
|
||||
await fillField(user, 'channel-name-textbox', 'gchat-channel');
|
||||
await fillField(user, 'webhook-url-textbox', validWebhookUrl);
|
||||
|
||||
await user.click(screen.getByTestId('save-channel-button'));
|
||||
|
||||
|
||||
@@ -130,6 +130,28 @@ describe('Footer utils', () => {
|
||||
};
|
||||
expect(validateCreateAlertState(currentArgs)).toBeNull();
|
||||
});
|
||||
|
||||
it('when threshold channels are null', () => {
|
||||
const currentArgs: BuildCreateAlertRulePayloadArgs = {
|
||||
...args,
|
||||
basicAlertState: {
|
||||
...args.basicAlertState,
|
||||
name: 'test name',
|
||||
},
|
||||
thresholdState: {
|
||||
...args.thresholdState,
|
||||
thresholds: [
|
||||
{
|
||||
...args.thresholdState.thresholds[0],
|
||||
channels: null as unknown as string[],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
expect(validateCreateAlertState(currentArgs)).toBe(
|
||||
'Please select at least one channel for each threshold or enable routing policies',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNotificationSettingsProps', () => {
|
||||
|
||||
@@ -44,7 +44,8 @@ export function validateCreateAlertState(
|
||||
if (!threshold.label) {
|
||||
return 'Please enter a label for each threshold';
|
||||
}
|
||||
if (!notificationSettings.routingPolicies && !threshold.channels.length) {
|
||||
// this runs during render, so a throw here takes down the whole page
|
||||
if (!notificationSettings.routingPolicies && !threshold.channels?.length) {
|
||||
return 'Please select at least one channel for each threshold or enable routing policies';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +316,34 @@ describe('CreateAlertV2 utils', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getThresholdStateFromAlertDef null channels', () => {
|
||||
it('falls back to an empty array so downstream consumers never see null', () => {
|
||||
const def: PostableAlertRuleV2 = {
|
||||
...defaultPostableAlertRuleV2,
|
||||
condition: {
|
||||
...defaultPostableAlertRuleV2.condition,
|
||||
thresholds: {
|
||||
kind: 'basic',
|
||||
spec: [
|
||||
{
|
||||
name: 'critical',
|
||||
target: 1,
|
||||
targetUnit: UniversalYAxisUnit.MINUTES,
|
||||
channels: null as unknown as string[],
|
||||
matchType: AlertThresholdMatchType.AT_LEAST_ONCE,
|
||||
op: AlertThresholdOperator.IS_ABOVE,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
getThresholdStateFromAlertDef(def).thresholds[0].channels,
|
||||
).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizeOperator', () => {
|
||||
it.each([
|
||||
['1', AlertThresholdOperator.IS_ABOVE],
|
||||
|
||||
@@ -258,7 +258,9 @@ export function getThresholdStateFromAlertDef(
|
||||
recoveryThresholdValue: null,
|
||||
unit: threshold.targetUnit,
|
||||
color: getColorForThreshold(threshold.name),
|
||||
channels: threshold.channels,
|
||||
// rules created outside the UI can come back with a null channels
|
||||
// field; drop the guard once the API enforces the schema
|
||||
channels: threshold.channels ?? [],
|
||||
})) || [],
|
||||
selectedQuery: alertDef.condition.selectedQueryName || '',
|
||||
operator:
|
||||
|
||||
@@ -82,7 +82,7 @@ export function getHostMetricsQueryPayload(
|
||||
start: number,
|
||||
end: number,
|
||||
): ReturnType<typeof getHostQueryPayload> {
|
||||
return getHostQueryPayload(host.hostName, start, end);
|
||||
return getHostQueryPayload(host.hostName, start, end, true);
|
||||
}
|
||||
|
||||
export { hostWidgetInfo };
|
||||
|
||||
@@ -562,13 +562,9 @@ export const getClusterMetricsQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `MAX(${INFRA_MONITORING_ATTR_KEYS.K8S_NODE_CONDITION_READY})`,
|
||||
op: '=',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
having: {
|
||||
expression: `max(${INFRA_MONITORING_ATTR_KEYS.K8S_NODE_CONDITION_READY}) = 1`,
|
||||
},
|
||||
legend: `{{${INFRA_MONITORING_ATTR_KEYS.K8S_NODE_NAME}}}`,
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
@@ -648,13 +644,9 @@ export const getClusterMetricsQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `MAX(${INFRA_MONITORING_ATTR_KEYS.K8S_NODE_CONDITION_READY})`,
|
||||
op: '=',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
having: {
|
||||
expression: `max(${INFRA_MONITORING_ATTR_KEYS.K8S_NODE_CONDITION_READY}) = 0`,
|
||||
},
|
||||
legend: `{{${INFRA_MONITORING_ATTR_KEYS.K8S_NODE_NAME}}}`,
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
|
||||
@@ -1208,13 +1208,9 @@ export const getNamespaceMetricsQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `MAX(${INFRA_MONITORING_ATTR_KEYS.K8S_REPLICASET_DESIRED})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
having: {
|
||||
expression: `max(${INFRA_MONITORING_ATTR_KEYS.K8S_REPLICASET_DESIRED}) > 0`,
|
||||
},
|
||||
legend: 'desired',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
@@ -1261,13 +1257,9 @@ export const getNamespaceMetricsQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `MAX(${INFRA_MONITORING_ATTR_KEYS.K8S_REPLICASET_DESIRED})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
having: {
|
||||
expression: `max(${INFRA_MONITORING_ATTR_KEYS.K8S_REPLICASET_AVAILABLE}) > 0`,
|
||||
},
|
||||
legend: 'available',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import type { Having } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import type { Having as HavingV5 } from 'types/api/v5/queryRange';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { DataSource, ReduceOperators } from 'types/common/queryBuilder';
|
||||
|
||||
const buildSumGreaterThanZeroHaving = (
|
||||
metricKey: string,
|
||||
useV5HavingFormat: boolean,
|
||||
): Having[] | HavingV5 =>
|
||||
useV5HavingFormat
|
||||
? { expression: `sum(${metricKey}) > 0` }
|
||||
: [{ columnName: `SUM(${metricKey})`, op: '>', value: 0 }];
|
||||
|
||||
export const getPodQueryPayload = (
|
||||
clusterName: string,
|
||||
podName: string,
|
||||
@@ -1540,6 +1550,7 @@ export const getHostQueryPayload = (
|
||||
hostName: string,
|
||||
start: number,
|
||||
end: number,
|
||||
useV5HavingFormat = false,
|
||||
): GetQueryResultsProps[] => {
|
||||
const hostNameKey = 'host.name';
|
||||
const cpuTimeKey = 'system.cpu.time';
|
||||
@@ -1802,13 +1813,7 @@ export const getHostQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `SUM(${fsUsageKey})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
having: buildSumGreaterThanZeroHaving(fsUsageKey, useV5HavingFormat),
|
||||
legend: '{{mountpoint}}',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
@@ -1857,13 +1862,7 @@ export const getHostQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `SUM(${fsUsageKey})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
having: buildSumGreaterThanZeroHaving(fsUsageKey, useV5HavingFormat),
|
||||
legend: '{{mountpoint}}',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
@@ -2089,13 +2088,7 @@ export const getHostQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `SUM(${netIoKey})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
having: buildSumGreaterThanZeroHaving(netIoKey, useV5HavingFormat),
|
||||
legend: '{{device}}::{{direction}}',
|
||||
limit: 30,
|
||||
orderBy: [],
|
||||
@@ -2551,13 +2544,7 @@ export const getHostQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `SUM(${diskOpsKey})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
having: buildSumGreaterThanZeroHaving(diskOpsKey, useV5HavingFormat),
|
||||
legend: '{{device}}::{{direction}}',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
@@ -2626,13 +2613,7 @@ export const getHostQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `SUM(${diskPendingKey})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
having: buildSumGreaterThanZeroHaving(diskPendingKey, useV5HavingFormat),
|
||||
legend: '{{device}}',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
@@ -2708,13 +2689,7 @@ export const getHostQueryPayload = (
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `SUM(${diskOpTimeKey})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
having: buildSumGreaterThanZeroHaving(diskOpTimeKey, useV5HavingFormat),
|
||||
legend: '{{device}}::{{direction}}',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
|
||||
@@ -387,4 +387,42 @@ describe('useOptionsMenu', () => {
|
||||
expect(remaining).toHaveLength(seedColumns.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fieldsSelector.value drops legacy columns without a name', () => {
|
||||
it('excludes entries missing name while keeping valid columns', () => {
|
||||
(useGetQueryKeySuggestions as jest.Mock).mockReturnValue({
|
||||
data: { data: { data: { keys: {} } } },
|
||||
isFetching: false,
|
||||
});
|
||||
(usePreferenceContext as jest.Mock).mockReturnValue({
|
||||
traces: {
|
||||
preferences: {
|
||||
columns: [
|
||||
{ name: 'body', fieldContext: 'log' },
|
||||
{ key: 'legacy-key-no-name', fieldContext: 'log' },
|
||||
{ name: 'timestamp', fieldContext: 'log' },
|
||||
],
|
||||
formatting: { format: 'table', maxLines: 1, fontSize: 'small' },
|
||||
},
|
||||
updateColumns: mockUpdateColumns,
|
||||
updateFormatting: mockUpdateFormatting,
|
||||
},
|
||||
logs: {
|
||||
preferences: { columns: [], formatting: {} },
|
||||
updateColumns: mockUpdateColumns,
|
||||
updateFormatting: mockUpdateFormatting,
|
||||
},
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useOptionsMenu({
|
||||
dataSource: DataSource.TRACES,
|
||||
aggregateOperator: 'count',
|
||||
}),
|
||||
);
|
||||
|
||||
const fields = result.current.config.fieldsSelector?.value ?? [];
|
||||
expect(fields.map((f) => f.name)).toStrictEqual(['body', 'timestamp']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -399,7 +399,7 @@ const useOptionsMenu = ({
|
||||
onReorder: reorderSelectColumns,
|
||||
},
|
||||
fieldsSelector: {
|
||||
value: preferences?.columns ?? [],
|
||||
value: preferences?.columns?.filter((item) => has(item, 'name')) ?? [],
|
||||
onFieldsChange: updateColumns,
|
||||
},
|
||||
format: {
|
||||
|
||||
@@ -80,7 +80,6 @@ function ResourceAttributesFilter({
|
||||
<div className="environment-selector">
|
||||
<Select
|
||||
getPopupContainer={popupContainer}
|
||||
key={selectedEnvironments.join('')}
|
||||
showSearch
|
||||
mode="multiple"
|
||||
value={selectedEnvironments}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { createMemoryHistory, MemoryHistory } from 'history';
|
||||
import { ResourceProvider } from 'hooks/useResourceAttribute';
|
||||
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
|
||||
import { encode } from 'js-base64';
|
||||
|
||||
import ResourceAttributesFilter from '../ResourceAttributesFilter';
|
||||
|
||||
jest.mock('lib/history', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
push: jest.fn(),
|
||||
location: { search: '', pathname: '/' },
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('api/metrics/getResourceAttributes', () => ({
|
||||
getResourceAttributesTagKeys: jest.fn(),
|
||||
getResourceAttributesTagValues: jest.fn(),
|
||||
}));
|
||||
|
||||
// eslint-disable-next-line import/first, import/order
|
||||
import {
|
||||
getResourceAttributesTagKeys,
|
||||
getResourceAttributesTagValues,
|
||||
// eslint-disable-next-line import/newline-after-import
|
||||
} from 'api/metrics/getResourceAttributes';
|
||||
// eslint-disable-next-line import/first, import/order
|
||||
import history from 'lib/history';
|
||||
|
||||
const mockTagKeys = getResourceAttributesTagKeys as jest.MockedFunction<
|
||||
typeof getResourceAttributesTagKeys
|
||||
>;
|
||||
const mockTagValues = getResourceAttributesTagValues as jest.MockedFunction<
|
||||
typeof getResourceAttributesTagValues
|
||||
>;
|
||||
|
||||
function tagKeysPayload(keys: string[]): never {
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'ok',
|
||||
payload: {
|
||||
data: {
|
||||
attributeKeys: keys.map((key) => ({
|
||||
key,
|
||||
dataType: 'string',
|
||||
type: 'resource',
|
||||
isColumn: false,
|
||||
})),
|
||||
},
|
||||
},
|
||||
} as unknown as never;
|
||||
}
|
||||
|
||||
function tagValuesPayload(values: string[]): never {
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'ok',
|
||||
payload: { data: { stringAttributeValues: values } },
|
||||
} as unknown as never;
|
||||
}
|
||||
|
||||
function seedUrl(queries: IResourceAttribute[], pathname: string): void {
|
||||
const location = history.location as { search: string; pathname: string };
|
||||
location.search = queries.length
|
||||
? `?resourceAttribute=${encode(JSON.stringify(queries))}`
|
||||
: '';
|
||||
location.pathname = pathname;
|
||||
}
|
||||
|
||||
function renderFilter(pathname: string): MemoryHistory {
|
||||
const routerHistory = createMemoryHistory({
|
||||
initialEntries: [`${pathname}${history.location.search}`],
|
||||
});
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
|
||||
function Wrapper({ children }: { children: ReactNode }): JSX.Element {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Router history={routerHistory}>
|
||||
<ResourceProvider>{children}</ResourceProvider>
|
||||
</Router>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
render(
|
||||
<Wrapper>
|
||||
<ResourceAttributesFilter />
|
||||
</Wrapper>,
|
||||
);
|
||||
|
||||
return routerHistory;
|
||||
}
|
||||
|
||||
describe('ResourceAttributesFilter', () => {
|
||||
beforeEach(() => {
|
||||
mockTagKeys.mockReset();
|
||||
mockTagValues.mockReset();
|
||||
mockTagKeys.mockResolvedValue(
|
||||
tagKeysPayload(['resource_deployment.environment']),
|
||||
);
|
||||
mockTagValues.mockResolvedValue(tagValuesPayload(['production', 'staging']));
|
||||
seedUrl([], '/');
|
||||
});
|
||||
|
||||
it('shows every applied filter on the service map, including ones it cannot apply', async () => {
|
||||
seedUrl(
|
||||
[
|
||||
{
|
||||
id: 'svc',
|
||||
tagKey: 'resource_service_name',
|
||||
operator: 'IN',
|
||||
tagValue: ['frontend'],
|
||||
},
|
||||
{
|
||||
id: 'env',
|
||||
tagKey: 'resource_deployment.environment',
|
||||
operator: 'IN',
|
||||
tagValue: ['production'],
|
||||
},
|
||||
],
|
||||
ROUTES.SERVICE_MAP,
|
||||
);
|
||||
|
||||
renderFilter(ROUTES.SERVICE_MAP);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText(/service\.name/)).toBeInTheDocument(),
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen
|
||||
.getByTestId('resource-environment-filter')
|
||||
.querySelector('.ant-select-selection-item'),
|
||||
).toHaveTextContent('production'),
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps the environment dropdown open so more than one environment can be picked', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderFilter('/services');
|
||||
|
||||
const environmentFilter = screen.getByTestId('resource-environment-filter');
|
||||
await user.click(
|
||||
environmentFilter.querySelector('input') as HTMLInputElement,
|
||||
);
|
||||
|
||||
await user.click(await screen.findByTitle('production'));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.getByTitle('staging').closest('.ant-select-dropdown'),
|
||||
).not.toHaveClass('ant-select-dropdown-hidden'),
|
||||
);
|
||||
|
||||
await user.click(screen.getByTitle('staging'));
|
||||
|
||||
await waitFor(() => {
|
||||
const selected = Array.from(
|
||||
environmentFilter.querySelectorAll('.ant-select-selection-item-content'),
|
||||
).map((node) => node.textContent);
|
||||
expect(selected).toStrictEqual(['production', 'staging']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,10 @@
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { encode } from 'js-base64';
|
||||
|
||||
import { whilelistedKeys } from './config';
|
||||
import { ResourceContext } from './context';
|
||||
import {
|
||||
IResourceAttribute,
|
||||
@@ -195,16 +193,9 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
setOptionsData({ mode: undefined, options: [] });
|
||||
}, [dispatchQueries]);
|
||||
|
||||
const getVisibleQueries = useMemo(() => {
|
||||
if (pathname === ROUTES.SERVICE_MAP) {
|
||||
return queries.filter((query) => whilelistedKeys.includes(query.tagKey));
|
||||
}
|
||||
return queries;
|
||||
}, [queries, pathname]);
|
||||
|
||||
const value: IResourceAttributeProps = useMemo(
|
||||
() => ({
|
||||
queries: getVisibleQueries,
|
||||
queries,
|
||||
staging,
|
||||
handleClearAll,
|
||||
handleClose,
|
||||
@@ -227,7 +218,7 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
staging,
|
||||
selectedQuery,
|
||||
optionsData,
|
||||
getVisibleQueries,
|
||||
queries,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -504,22 +504,23 @@ describe('ResourceProvider', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVisibleQueries (SERVICE_MAP filtering)', () => {
|
||||
it('filters queries down to whitelisted keys on SERVICE_MAP', () => {
|
||||
const seeded = [
|
||||
{
|
||||
id: 'a',
|
||||
tagKey: 'resource_service_name',
|
||||
operator: 'IN',
|
||||
tagValue: ['frontend'],
|
||||
},
|
||||
{
|
||||
id: 'b',
|
||||
tagKey: 'resource_k8s_cluster_name',
|
||||
operator: 'IN',
|
||||
tagValue: ['prod'],
|
||||
},
|
||||
];
|
||||
describe('SERVICE_MAP', () => {
|
||||
const seeded = [
|
||||
{
|
||||
id: 'a',
|
||||
tagKey: 'resource_service_name',
|
||||
operator: 'IN',
|
||||
tagValue: ['frontend'],
|
||||
},
|
||||
{
|
||||
id: 'b',
|
||||
tagKey: 'resource_k8s_cluster_name',
|
||||
operator: 'IN',
|
||||
tagValue: ['prod'],
|
||||
},
|
||||
];
|
||||
|
||||
it('exposes every query from the URL, including ones the map cannot apply', () => {
|
||||
mockLibHistory(
|
||||
`?resourceAttribute=${encode(JSON.stringify(seeded))}`,
|
||||
ROUTES.SERVICE_MAP,
|
||||
@@ -532,24 +533,10 @@ describe('ResourceProvider', () => {
|
||||
wrapper: createWrapper({ routerHistory }),
|
||||
});
|
||||
|
||||
expect(result.current.queries).toStrictEqual([seeded[1]]);
|
||||
expect(result.current.queries).toStrictEqual(seeded);
|
||||
});
|
||||
|
||||
it('returns all queries on non-SERVICE_MAP routes', () => {
|
||||
const seeded = [
|
||||
{
|
||||
id: 'a',
|
||||
tagKey: 'resource_service_name',
|
||||
operator: 'IN',
|
||||
tagValue: ['frontend'],
|
||||
},
|
||||
{
|
||||
id: 'b',
|
||||
tagKey: 'resource_k8s_cluster_name',
|
||||
operator: 'IN',
|
||||
tagValue: ['prod'],
|
||||
},
|
||||
];
|
||||
mockLibHistory(
|
||||
`?resourceAttribute=${encode(JSON.stringify(seeded))}`,
|
||||
'/services',
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import ROUTES from 'constants/routes';
|
||||
|
||||
import { whilelistedKeys } from '../config';
|
||||
import { mappingWithRoutesAndKeys } from '../utils';
|
||||
import {
|
||||
filterServiceMapSupportedQueries,
|
||||
mappingWithRoutesAndKeys,
|
||||
} from '../utils';
|
||||
|
||||
describe('useResourceAttribute config', () => {
|
||||
describe('whilelistedKeys', () => {
|
||||
@@ -74,4 +77,29 @@ describe('useResourceAttribute config', () => {
|
||||
expect(result).toStrictEqual(allFilters);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterServiceMapSupportedQueries', () => {
|
||||
const environmentQuery = {
|
||||
id: 'env',
|
||||
tagKey: 'resource_deployment_environment',
|
||||
operator: 'IN',
|
||||
tagValue: ['production'],
|
||||
};
|
||||
const serviceQuery = {
|
||||
id: 'svc',
|
||||
tagKey: 'resource_service_name',
|
||||
operator: 'IN',
|
||||
tagValue: ['frontend'],
|
||||
};
|
||||
|
||||
it('should keep only the queries the service map can filter on', () => {
|
||||
expect(
|
||||
filterServiceMapSupportedQueries([environmentQuery, serviceQuery]),
|
||||
).toStrictEqual([environmentQuery]);
|
||||
});
|
||||
|
||||
it('should return an empty list when no query is supported', () => {
|
||||
expect(filterServiceMapSupportedQueries([serviceQuery])).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -281,3 +281,8 @@ export const mappingWithRoutesAndKeys = (
|
||||
}
|
||||
return filters;
|
||||
};
|
||||
|
||||
export const filterServiceMapSupportedQueries = (
|
||||
queries: IResourceAttribute[],
|
||||
): IResourceAttribute[] =>
|
||||
queries.filter((query) => whilelistedKeys.includes(query.tagKey));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//@ts-nocheck
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
@@ -11,6 +11,7 @@ import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
|
||||
import useResourceAttribute from 'hooks/useResourceAttribute';
|
||||
import { whilelistedKeys } from 'hooks/useResourceAttribute/config';
|
||||
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
|
||||
import { filterServiceMapSupportedQueries } from 'hooks/useResourceAttribute/utils';
|
||||
import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions';
|
||||
import { AppState } from 'store/reducers';
|
||||
import styled from 'styled-components';
|
||||
@@ -70,32 +71,37 @@ function ServiceMap(props: ServiceMapProps): JSX.Element {
|
||||
|
||||
const { queries } = useResourceAttribute();
|
||||
|
||||
const supportedQueries = useMemo(
|
||||
() => filterServiceMapSupportedQueries(queries),
|
||||
[queries],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
/*
|
||||
Call the apis only when the route is loaded.
|
||||
Check this issue: https://github.com/SigNoz/signoz/issues/110
|
||||
*/
|
||||
getDetailedServiceMapItems(globalTime, queries);
|
||||
}, [globalTime, getDetailedServiceMapItems, queries]);
|
||||
getDetailedServiceMapItems(globalTime, supportedQueries);
|
||||
}, [globalTime, getDetailedServiceMapItems, supportedQueries]);
|
||||
|
||||
useEffect(() => {
|
||||
fgRef.current && fgRef.current.d3Force('charge').strength(-400);
|
||||
});
|
||||
|
||||
if (serviceMap.loading) {
|
||||
return <Spinner size="large" tip="Loading..." />;
|
||||
}
|
||||
const renderBody = (): JSX.Element => {
|
||||
if (serviceMap.loading) {
|
||||
return <Spinner size="large" tip="Loading..." />;
|
||||
}
|
||||
|
||||
if (serviceMap.items.length === 0) {
|
||||
return <Card>No Service Found</Card>;
|
||||
}
|
||||
|
||||
return <Map fgRef={fgRef} serviceMap={serviceMap} />;
|
||||
};
|
||||
|
||||
if (!serviceMap.loading && serviceMap.items.length === 0) {
|
||||
return (
|
||||
<Container>
|
||||
<ResourceAttributesFilter />
|
||||
<Card>No Service Found</Card>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="service-map-container">
|
||||
<Container className="service-map-container">
|
||||
<ResourceAttributesFilter
|
||||
suffixIcon={
|
||||
<TextToolTip
|
||||
@@ -108,8 +114,8 @@ function ServiceMap(props: ServiceMapProps): JSX.Element {
|
||||
}
|
||||
/>
|
||||
|
||||
<Map fgRef={fgRef} serviceMap={serviceMap} />
|
||||
</div>
|
||||
{renderBody()}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,40 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
if err := router.Handle("/api/v1/invite", handler.New(provider.authzMiddleware.AdminAccess(provider.userHandler.CreateInvite), handler.OpenAPIDef{
|
||||
ID: "CreateInvite",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Create invite",
|
||||
Description: "This endpoint creates an invite for a user",
|
||||
Request: new(types.PostableInvite),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(types.Invite),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||
Deprecated: true,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user", handler.New(provider.authzMiddleware.AdminAccess(provider.userHandler.ListUsersDeprecated), handler.OpenAPIDef{
|
||||
ID: "ListUsersDeprecated",
|
||||
Tags: []string{"users"},
|
||||
Summary: "List users",
|
||||
Description: "This endpoint lists all users",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: make([]*types.DeprecatedUser, 0),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: true,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users", handler.New(provider.authzMiddleware.AdminAccess(provider.userHandler.ListUsers), handler.OpenAPIDef{
|
||||
ID: "ListUsers",
|
||||
Tags: []string{"users"},
|
||||
@@ -61,23 +27,6 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user/me", handler.New(provider.authzMiddleware.OpenAccess(provider.userHandler.GetMyUserDeprecated), handler.OpenAPIDef{
|
||||
ID: "GetMyUserDeprecated",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get my user",
|
||||
Description: "This endpoint returns the user I belong to",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(types.DeprecatedUser),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: true,
|
||||
SecuritySchemes: []handler.OpenAPISecurityScheme{{Name: authtypes.IdentNProviderTokenizer.StringValue()}},
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/me", handler.New(provider.authzMiddleware.OpenAccess(provider.userHandler.GetMyUser), handler.OpenAPIDef{
|
||||
ID: "GetMyUser",
|
||||
Tags: []string{"users"},
|
||||
@@ -180,23 +129,6 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/getResetPasswordToken/{id}", handler.New(provider.authzMiddleware.AdminAccess(provider.userHandler.GetResetPasswordTokenDeprecated), handler.OpenAPIDef{
|
||||
ID: "GetResetPasswordTokenDeprecated",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get reset password token",
|
||||
Description: "This endpoint returns the reset password token by id",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(types.ResetPasswordToken),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: true,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/{id}/reset_password_tokens", handler.New(provider.authzMiddleware.AdminAccess(provider.userHandler.GetResetPasswordToken), handler.OpenAPIDef{
|
||||
ID: "GetResetPasswordToken",
|
||||
Tags: []string{"users"},
|
||||
@@ -248,23 +180,6 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/resetPassword", handler.New(provider.authzMiddleware.OpenAccess(provider.userHandler.ResetPassword), handler.OpenAPIDef{
|
||||
ID: "ResetPasswordDeprecated",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Reset password",
|
||||
Description: "This endpoint resets the password by token",
|
||||
Request: new(types.PostableResetPassword),
|
||||
RequestContentType: "application/json",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||
Deprecated: true,
|
||||
SecuritySchemes: []handler.OpenAPISecurityScheme{},
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/me/factor_password", handler.New(provider.authzMiddleware.OpenAccess(provider.userHandler.ChangePassword), handler.OpenAPIDef{
|
||||
ID: "UpdateMyPassword",
|
||||
Tags: []string{"users"},
|
||||
@@ -333,40 +248,6 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/{id}/roles", handler.New(provider.authzMiddleware.AdminAccess(provider.userHandler.SetRoleByUserID), handler.OpenAPIDef{
|
||||
ID: "SetRoleByUserID",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Set user roles",
|
||||
Description: "This endpoint assigns the role to the user roles by user id",
|
||||
Request: new(types.PostableRole),
|
||||
RequestContentType: "application/json",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||
Deprecated: true,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/{id}/roles/{roleId}", handler.New(provider.authzMiddleware.AdminAccess(provider.userHandler.RemoveUserRoleByRoleID), handler.OpenAPIDef{
|
||||
ID: "RemoveUserRoleByUserIDAndRoleID",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Remove a role from user",
|
||||
Description: "This endpoint removes a role from the user by user id and role id",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||
Deprecated: true,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/roles/{id}/users", handler.New(provider.authzMiddleware.AdminAccess(provider.userHandler.GetUsersByRoleID), handler.OpenAPIDef{
|
||||
ID: "GetUsersByRoleID",
|
||||
Tags: []string{"users"},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"><defs><style>.cls-1{fill:#aecbfa;}.cls-1,.cls-2,.cls-3{fill-rule:evenodd;}.cls-2{fill:#669df6;}.cls-3{fill:#4285f4;}</style></defs><title>Icon_24px_SQL_Color</title><g data-name="Product Icons"><g ><polygon class="cls-1" points="4.67 10.44 4.67 13.45 12 17.35 12 14.34 4.67 10.44"/><polygon class="cls-1" points="4.67 15.09 4.67 18.1 12 22 12 18.99 4.67 15.09"/><polygon class="cls-2" points="12 17.35 19.33 13.45 19.33 10.44 12 14.34 12 17.35"/><polygon class="cls-2" points="12 22 19.33 18.1 19.33 15.09 12 18.99 12 22"/><polygon class="cls-3" points="19.33 8.91 19.33 5.9 12 2 12 5.01 19.33 8.91"/><polygon class="cls-2" points="12 2 4.67 5.9 4.67 8.91 12 5.01 12 2"/><polygon class="cls-1" points="4.67 5.87 4.67 8.89 12 12.79 12 9.77 4.67 5.87"/><polygon class="cls-2" points="12 12.79 19.33 8.89 19.33 5.87 12 9.77 12 12.79"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 933 B |
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"id": "cloudsql_mysql",
|
||||
"title": "GCP Cloud SQL for MySQL",
|
||||
"icon": "file://icon.svg",
|
||||
"overview": "file://overview.md",
|
||||
"supportedSignals": {
|
||||
"metrics": true,
|
||||
"logs": true
|
||||
},
|
||||
"dataCollected": {
|
||||
"metrics": [
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/up",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/instance_state",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/replication/replica_lag",
|
||||
"unit": "Seconds",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/cpu/utilization",
|
||||
"unit": "Percent",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/memory/utilization",
|
||||
"unit": "Percent",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/disk/utilization",
|
||||
"unit": "Percent",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/network/connections",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/mysql/queries",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/mysql/dml_operations_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/mysql/threads",
|
||||
"unit": "Count",
|
||||
"type": "Gauge",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/mysql/innodb/buffer_pool_reads_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/mysql/innodb/buffer_pool_read_requests_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/mysql/slow_queries_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/mysql/aborted_connects_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/mysql/innodb/deadlocks_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/mysql/innodb/row_lock_waits_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/disk/read_ops_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloudsql.googleapis.com/database/disk/write_ops_count",
|
||||
"unit": "Count",
|
||||
"type": "Sum",
|
||||
"description": ""
|
||||
}
|
||||
],
|
||||
"logs": []
|
||||
},
|
||||
"telemetryCollectionStrategy": {
|
||||
"gcp": {}
|
||||
},
|
||||
"assets": {
|
||||
"dashboards": [
|
||||
{
|
||||
"id": "overview",
|
||||
"title": "GCP Cloud SQL for MySQL Overview",
|
||||
"description": "Overview of GCP Cloud SQL for MySQL metrics",
|
||||
"definition": "file://assets/dashboards/overview.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
### Monitor GCP Cloud SQL for MySQL with SigNoz
|
||||
|
||||
Collect key GCP Cloud SQL for MySQL metrics and view them with an out of the box dashboard.
|
||||
@@ -784,40 +784,57 @@
|
||||
{
|
||||
"kind": "time_series",
|
||||
"spec": {
|
||||
"name": "A",
|
||||
"plugin": {
|
||||
"kind": "signoz/BuilderQuery",
|
||||
"kind": "signoz/CompositeQuery",
|
||||
"spec": {
|
||||
"name": "A",
|
||||
"signal": "metrics",
|
||||
"source": "",
|
||||
"aggregations": [
|
||||
"queries": [
|
||||
{
|
||||
"metricName": "cloudsql.googleapis.com/database/cpu/utilization",
|
||||
"temporality": "",
|
||||
"timeAggregation": "max",
|
||||
"spaceAggregation": "max",
|
||||
"reduceTo": "avg"
|
||||
}
|
||||
],
|
||||
"disabled": false,
|
||||
"filter": {
|
||||
"expression": "project_id = $project_id AND database_id in $database_id AND gcp.resource_type = 'cloudsql_database' "
|
||||
},
|
||||
"groupBy": [
|
||||
"type": "builder_query",
|
||||
"spec": {
|
||||
"name": "A",
|
||||
"stepInterval": 0,
|
||||
"signal": "metrics",
|
||||
"source": "",
|
||||
"aggregations": [
|
||||
{
|
||||
"metricName": "cloudsql.googleapis.com/database/cpu/utilization",
|
||||
"temporality": "",
|
||||
"timeAggregation": "max",
|
||||
"spaceAggregation": "max",
|
||||
"reduceTo": ""
|
||||
}
|
||||
],
|
||||
"disabled": false,
|
||||
"filter": {
|
||||
"expression": "project_id = $project_id AND database_id in $database_id AND gcp.resource_type = 'cloudsql_database' "
|
||||
},
|
||||
"groupBy": [
|
||||
{
|
||||
"name": "database_id",
|
||||
"signal": "",
|
||||
"fieldContext": "attribute",
|
||||
"fieldDataType": "string"
|
||||
}
|
||||
],
|
||||
"order": null,
|
||||
"selectFields": null,
|
||||
"secondaryAggregations": null,
|
||||
"functions": null,
|
||||
"legend": "{{database_id}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "database_id",
|
||||
"signal": "",
|
||||
"fieldContext": "attribute",
|
||||
"fieldDataType": "string"
|
||||
"type": "builder_formula",
|
||||
"spec": {
|
||||
"name": "F1",
|
||||
"expression": "100 * A",
|
||||
"disabled": false,
|
||||
"order": null,
|
||||
"functions": null,
|
||||
"legend": "{{database_id}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"order": [],
|
||||
"having": {
|
||||
"expression": ""
|
||||
},
|
||||
"functions": [],
|
||||
"legend": "{{database_id}}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1417,4 +1434,4 @@
|
||||
"refreshInterval": "",
|
||||
"links": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,53 +37,6 @@ func (module *getter) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID)
|
||||
return rootUser, userRoles, nil
|
||||
}
|
||||
|
||||
func (module *getter) ListDeprecatedUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*types.DeprecatedUser, error) {
|
||||
users, err := module.store.ListUsersByOrgID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// filter root users if feature flag `hide_root_users` is true
|
||||
evalCtx := featuretypes.NewFlaggerEvaluationContext(orgID)
|
||||
hideRootUsers := module.flagger.BooleanOrEmpty(ctx, flagger.FeatureHideRootUser, evalCtx)
|
||||
|
||||
if hideRootUsers {
|
||||
users = slices.DeleteFunc(users, func(user *types.User) bool { return user.IsRoot })
|
||||
}
|
||||
|
||||
userIDs := make([]valuer.UUID, len(users))
|
||||
for idx, user := range users {
|
||||
userIDs[idx] = user.ID
|
||||
}
|
||||
|
||||
userRoles, err := module.userRoleStore.ListUserRolesByOrgIDAndUserIDs(ctx, orgID, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build userID → role name mapping directly from the joined Role
|
||||
userIDToRoleNames := make(map[valuer.UUID][]string)
|
||||
for _, ur := range userRoles {
|
||||
if ur.Role != nil {
|
||||
userIDToRoleNames[ur.UserID] = append(userIDToRoleNames[ur.UserID], ur.Role.Name)
|
||||
}
|
||||
}
|
||||
|
||||
deprecatedUsers := make([]*types.DeprecatedUser, 0, len(users))
|
||||
for _, user := range users {
|
||||
roleNames := userIDToRoleNames[user.ID]
|
||||
|
||||
if len(roleNames) == 0 {
|
||||
return nil, errors.Newf(errors.TypeInternal, authtypes.ErrCodeUserRolesNotFound, "no user roles entries found for user: %s", user.ID.String())
|
||||
}
|
||||
|
||||
role := authtypes.SigNozManagedRoleToExistingLegacyRole[roleNames[0]]
|
||||
deprecatedUsers = append(deprecatedUsers, types.NewDeprecatedUserFromUserAndRole(user, role))
|
||||
}
|
||||
|
||||
return deprecatedUsers, nil
|
||||
}
|
||||
|
||||
func (module *getter) ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*types.User, error) {
|
||||
users, err := module.store.ListUsersByOrgID(ctx, orgID)
|
||||
if err != nil {
|
||||
@@ -101,58 +54,10 @@ func (module *getter) ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) (
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (module *getter) GetDeprecatedUserByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*types.DeprecatedUser, error) {
|
||||
user, err := module.store.GetByOrgIDAndID(ctx, orgID, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userRoles, err := module.GetRolesByUserID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(userRoles) == 0 {
|
||||
return nil, errors.New(errors.TypeInternal, authtypes.ErrCodeUserRolesNotFound, "no user roles entries found")
|
||||
}
|
||||
|
||||
if userRoles[0].Role == nil {
|
||||
return nil, errors.New(errors.TypeInternal, authtypes.ErrCodeRoleNotFound, "role not found for user role entry")
|
||||
}
|
||||
|
||||
role := authtypes.SigNozManagedRoleToExistingLegacyRole[userRoles[0].Role.Name]
|
||||
|
||||
return types.NewDeprecatedUserFromUserAndRole(user, role), nil
|
||||
}
|
||||
|
||||
func (module *getter) GetUserByOrgIDAndID(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) (*types.User, error) {
|
||||
return module.store.GetByOrgIDAndID(ctx, orgID, userID)
|
||||
}
|
||||
|
||||
func (module *getter) Get(ctx context.Context, id valuer.UUID) (*types.DeprecatedUser, error) {
|
||||
user, err := module.store.GetUser(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userRoles, err := module.GetRolesByUserID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(userRoles) == 0 {
|
||||
return nil, errors.New(errors.TypeInternal, authtypes.ErrCodeUserRolesNotFound, "no user roles entries found")
|
||||
}
|
||||
|
||||
if userRoles[0].Role == nil {
|
||||
return nil, errors.New(errors.TypeInternal, authtypes.ErrCodeRoleNotFound, "role not found for user role entry")
|
||||
}
|
||||
|
||||
role := authtypes.SigNozManagedRoleToExistingLegacyRole[userRoles[0].Role.Name]
|
||||
|
||||
return types.NewDeprecatedUserFromUserAndRole(user, role), nil
|
||||
}
|
||||
|
||||
func (module *getter) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*types.User, error) {
|
||||
return module.store.ListUsersByEmailAndOrgIDs(ctx, email, orgIDs)
|
||||
}
|
||||
|
||||
@@ -61,33 +61,6 @@ func (handler *handler) CreateUser(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Success(rw, http.StatusCreated, types.Identifiable{ID: user.ID})
|
||||
}
|
||||
|
||||
func (handler *handler) CreateInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
var req types.PostableInvite
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
invites, err := handler.setter.CreateBulkInvite(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.IdentityID()), valuer.MustNewEmail(claims.Email), &types.PostableBulkInviteRequest{
|
||||
Invites: []types.PostableInvite{req},
|
||||
})
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusCreated, invites[0])
|
||||
}
|
||||
|
||||
func (handler *handler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
@@ -120,25 +93,6 @@ func (handler *handler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, userWithRoles)
|
||||
}
|
||||
|
||||
func (handler *handler) GetMyUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := handler.getter.GetDeprecatedUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.UserID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (handler *handler) GetMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
@@ -194,25 +148,6 @@ func (handler *handler) UpdateMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (handler *handler) ListUsersDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
users, err := handler.getter.ListDeprecatedUsersByOrgID(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, users)
|
||||
}
|
||||
|
||||
func (handler *handler) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
@@ -284,33 +219,6 @@ func (handler *handler) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (handler *handler) GetResetPasswordTokenDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
id := mux.Vars(r)["id"]
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := handler.getter.GetDeprecatedUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(id))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := handler.setter.GetOrCreateResetPasswordToken(ctx, user.ID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, token)
|
||||
}
|
||||
|
||||
func (handler *handler) GetResetPasswordToken(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
@@ -481,68 +389,6 @@ func (handler *handler) GetRolesByUserID(w http.ResponseWriter, r *http.Request)
|
||||
render.Success(w, http.StatusOK, roles)
|
||||
}
|
||||
|
||||
func (handler *handler) SetRoleByUserID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
userID := mux.Vars(r)["id"]
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if userID == claims.UserID {
|
||||
render.Error(w, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "users cannot call this api on self"))
|
||||
return
|
||||
}
|
||||
|
||||
postableRole := new(types.PostableRole)
|
||||
if err := json.NewDecoder(r.Body).Decode(postableRole); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if postableRole.Name == "" {
|
||||
render.Error(w, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "role name is required"))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := handler.setter.AddUserRole(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID), postableRole.Name); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
func (handler *handler) RemoveUserRoleByRoleID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
userID := mux.Vars(r)["id"]
|
||||
roleID := mux.Vars(r)["roleId"]
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if userID == claims.UserID {
|
||||
render.Error(w, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "users cannot call this api on self"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := handler.setter.RemoveUserRole(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID), valuer.MustNewUUID(roleID)); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (handler *handler) GetUsersByRoleID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -55,119 +55,6 @@ func NewSetter(store types.UserStore, tokenizer tokenizer.Tokenizer, emailing em
|
||||
}
|
||||
}
|
||||
|
||||
// CreateBulk implements invite.Module.
|
||||
func (module *setter) CreateBulkInvite(ctx context.Context, orgID valuer.UUID, identityID valuer.UUID, identityEmail valuer.Email, bulkInvites *types.PostableBulkInviteRequest) ([]*types.Invite, error) {
|
||||
// validate all emails to be invited
|
||||
emails := make([]string, len(bulkInvites.Invites))
|
||||
for idx, invite := range bulkInvites.Invites {
|
||||
emails[idx] = invite.Email.StringValue()
|
||||
}
|
||||
users, err := module.store.GetUsersByEmailsOrgIDAndStatuses(ctx, orgID, emails, []string{types.UserStatusActive.StringValue(), types.UserStatusPendingInvite.StringValue()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(users) > 0 {
|
||||
if err := users[0].ErrIfRoot(); err != nil {
|
||||
return nil, errors.WithAdditionalf(err, "Cannot send invite to root user")
|
||||
}
|
||||
|
||||
if users[0].Status == types.UserStatusPendingInvite {
|
||||
return nil, errors.Newf(errors.TypeAlreadyExists, errors.CodeAlreadyExists, "An invite already exists for this email: %s", users[0].Email.StringValue())
|
||||
}
|
||||
|
||||
return nil, errors.Newf(errors.TypeAlreadyExists, errors.CodeAlreadyExists, "User already exists with this email: %s", users[0].Email.StringValue())
|
||||
}
|
||||
|
||||
type userWithResetToken struct {
|
||||
User *types.User
|
||||
ResetPasswordToken *types.ResetPasswordToken
|
||||
Role types.Role
|
||||
}
|
||||
|
||||
newUsersWithResetToken := make([]*userWithResetToken, len(bulkInvites.Invites))
|
||||
|
||||
if err := module.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||
for idx, invite := range bulkInvites.Invites {
|
||||
// create a new user with pending invite status
|
||||
newUser, err := types.NewUser(invite.Name, invite.Email, orgID, types.UserStatusPendingInvite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// store the user and password in db
|
||||
err = module.createUserWithoutGrant(ctx, newUser, root.WithRoleNames([]string{authtypes.MustGetSigNozManagedRoleFromExistingRole(invite.Role)}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// generate reset password token
|
||||
resetPasswordToken, err := module.GetOrCreateResetPasswordToken(ctx, newUser.ID)
|
||||
if err != nil {
|
||||
module.settings.Logger().ErrorContext(ctx, "failed to create reset password token for invited user", errors.Attr(err))
|
||||
return err
|
||||
}
|
||||
|
||||
newUsersWithResetToken[idx] = &userWithResetToken{
|
||||
User: newUser,
|
||||
ResetPasswordToken: resetPasswordToken,
|
||||
Role: invite.Role,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
invites := make([]*types.Invite, len(bulkInvites.Invites))
|
||||
|
||||
// send password reset emails to all the invited users
|
||||
for idx, userWithToken := range newUsersWithResetToken {
|
||||
module.analytics.TrackUser(ctx, orgID.String(), identityID.String(), "Invite Sent", map[string]any{
|
||||
"invitee_email": userWithToken.User.Email,
|
||||
"invitee_role": userWithToken.Role,
|
||||
})
|
||||
|
||||
invite := &types.Invite{
|
||||
Identifiable: types.Identifiable{
|
||||
ID: userWithToken.User.ID,
|
||||
},
|
||||
Name: userWithToken.User.DisplayName,
|
||||
Email: userWithToken.User.Email,
|
||||
Token: userWithToken.ResetPasswordToken.Token,
|
||||
Role: userWithToken.Role,
|
||||
OrgID: userWithToken.User.OrgID,
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: userWithToken.User.CreatedAt,
|
||||
UpdatedAt: userWithToken.User.UpdatedAt,
|
||||
},
|
||||
}
|
||||
|
||||
invites[idx] = invite
|
||||
|
||||
frontendBaseUrl := bulkInvites.Invites[idx].FrontendBaseUrl
|
||||
if frontendBaseUrl == "" {
|
||||
module.settings.Logger().InfoContext(ctx, "frontend base url is not provided, skipping email", slog.Any("invitee_email", userWithToken.User.Email))
|
||||
continue
|
||||
}
|
||||
|
||||
resetLink := userWithToken.ResetPasswordToken.FactorPasswordResetLink(frontendBaseUrl)
|
||||
|
||||
tokenLifetime := module.config.Password.Invite.MaxTokenLifetime
|
||||
humanizedTokenLifetime := strings.TrimSpace(humanize.RelTime(time.Now(), time.Now().Add(tokenLifetime), "", ""))
|
||||
|
||||
if err := module.emailing.SendHTML(ctx, userWithToken.User.Email.String(), "You're Invited to Join SigNoz", emailtypes.TemplateNameInvitationEmail, map[string]any{
|
||||
"inviter_email": identityEmail.StringValue(),
|
||||
"link": resetLink,
|
||||
"Expiry": humanizedTokenLifetime,
|
||||
}); err != nil {
|
||||
module.settings.Logger().ErrorContext(ctx, "failed to send invite email", errors.Attr(err))
|
||||
}
|
||||
}
|
||||
|
||||
return invites, nil
|
||||
}
|
||||
|
||||
func (module *setter) CreateUser(ctx context.Context, user *types.User, opts ...root.CreateUserOption) error {
|
||||
createUserOpts := root.NewCreateUserOptions(opts...)
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type userRoleStore struct {
|
||||
@@ -19,26 +18,6 @@ func NewUserRoleStore(sqlstore sqlstore.SQLStore, settings factory.ProviderSetti
|
||||
return &userRoleStore{sqlstore: sqlstore, settings: settings}
|
||||
}
|
||||
|
||||
func (store *userRoleStore) ListUserRolesByOrgIDAndUserIDs(ctx context.Context, orgID valuer.UUID, userIDs []valuer.UUID) ([]*authtypes.UserRole, error) {
|
||||
userRoles := make([]*authtypes.UserRole, 0)
|
||||
|
||||
err := store.sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewSelect().
|
||||
Model(&userRoles).
|
||||
Join("JOIN users").
|
||||
JoinOn("users.id = user_role.user_id").
|
||||
Where("users.org_id = ?", orgID).
|
||||
Where("users.id IN (?)", bun.In(userIDs)).
|
||||
Relation("Role").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return userRoles, nil
|
||||
}
|
||||
|
||||
func (store *userRoleStore) GetUserRoleByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*authtypes.UserRole, error) {
|
||||
userRole := new(authtypes.UserRole)
|
||||
|
||||
|
||||
@@ -40,9 +40,6 @@ type Setter interface {
|
||||
UpdateAnyUser(ctx context.Context, orgID valuer.UUID, user *types.User) error
|
||||
DeleteUser(ctx context.Context, orgID valuer.UUID, id string, deletedBy string) error
|
||||
|
||||
// invite
|
||||
CreateBulkInvite(ctx context.Context, orgID valuer.UUID, identityID valuer.UUID, identityEmail valuer.Email, bulkInvites *types.PostableBulkInviteRequest) ([]*types.Invite, error)
|
||||
|
||||
// Creates a pending invite user with the roles given via opts and emails them the invite link.
|
||||
CreatePendingInviteUser(ctx context.Context, identityID valuer.UUID, identityEmail valuer.Email, frontendBaseURL string, user *types.User, opts ...CreateUserOption) (*types.User, error)
|
||||
|
||||
@@ -60,16 +57,11 @@ type Getter interface {
|
||||
GetRootUserByOrgID(context.Context, valuer.UUID) (*types.User, []*authtypes.UserRole, error)
|
||||
|
||||
// Get gets the users based on the given org id
|
||||
ListDeprecatedUsersByOrgID(context.Context, valuer.UUID) ([]*types.DeprecatedUser, error)
|
||||
ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*types.User, error)
|
||||
|
||||
// Get deprecated user object by orgID and id.
|
||||
GetDeprecatedUserByOrgIDAndID(context.Context, valuer.UUID, valuer.UUID) (*types.DeprecatedUser, error)
|
||||
// Get user by orgID and id.
|
||||
GetUserByOrgIDAndID(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) (*types.User, error)
|
||||
|
||||
// Get user by id.
|
||||
Get(context.Context, valuer.UUID) (*types.DeprecatedUser, error)
|
||||
|
||||
// List users by email and org ids.
|
||||
ListUsersByEmailAndOrgIDs(context.Context, valuer.Email, []valuer.UUID) ([]*types.User, error)
|
||||
|
||||
@@ -105,22 +97,15 @@ type Getter interface {
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
// invite
|
||||
CreateInvite(http.ResponseWriter, *http.Request)
|
||||
|
||||
// users
|
||||
ListUsersDeprecated(http.ResponseWriter, *http.Request)
|
||||
ListUsers(http.ResponseWriter, *http.Request)
|
||||
CreateUser(http.ResponseWriter, *http.Request)
|
||||
UpdateUser(http.ResponseWriter, *http.Request)
|
||||
DeleteUser(http.ResponseWriter, *http.Request)
|
||||
GetUser(http.ResponseWriter, *http.Request)
|
||||
GetMyUserDeprecated(http.ResponseWriter, *http.Request)
|
||||
GetMyUser(http.ResponseWriter, *http.Request)
|
||||
UpdateMyUser(http.ResponseWriter, *http.Request)
|
||||
GetRolesByUserID(http.ResponseWriter, *http.Request)
|
||||
SetRoleByUserID(http.ResponseWriter, *http.Request)
|
||||
RemoveUserRoleByRoleID(http.ResponseWriter, *http.Request)
|
||||
GetUsersByRoleID(http.ResponseWriter, *http.Request)
|
||||
|
||||
// user roles
|
||||
@@ -129,7 +114,6 @@ type Handler interface {
|
||||
DeleteUserRole(http.ResponseWriter, *http.Request)
|
||||
|
||||
// Reset Password
|
||||
GetResetPasswordTokenDeprecated(http.ResponseWriter, *http.Request)
|
||||
GetResetPasswordToken(http.ResponseWriter, *http.Request)
|
||||
CreateResetPasswordToken(http.ResponseWriter, *http.Request)
|
||||
VerifyResetPasswordToken(http.ResponseWriter, *http.Request)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -478,11 +479,19 @@ func (q *promqlQuery) toResult(matrix promql.Matrix, warnings []string, began ti
|
||||
|
||||
for idx := range v.Floats {
|
||||
p := v.Floats[idx]
|
||||
// NaN and +/-Inf have no JSON number form and nothing to plot; the
|
||||
// builder path drops them while scanning rows (see consume.go).
|
||||
if math.IsNaN(p.F) || math.IsInf(p.F, 0) {
|
||||
continue
|
||||
}
|
||||
s.Values = append(s.Values, &qbv5.TimeSeriesValue{
|
||||
Timestamp: p.T,
|
||||
Value: p.F,
|
||||
})
|
||||
}
|
||||
if len(s.Values) == 0 {
|
||||
continue
|
||||
}
|
||||
series = append(series, &s)
|
||||
}
|
||||
|
||||
@@ -494,13 +503,11 @@ func (q *promqlQuery) toResult(matrix promql.Matrix, warnings []string, began ti
|
||||
}
|
||||
statsMu.Unlock()
|
||||
|
||||
tsData := &qbv5.TimeSeriesData{
|
||||
QueryName: q.query.Name,
|
||||
Aggregations: []*qbv5.AggregationBucket{
|
||||
{
|
||||
Series: series,
|
||||
},
|
||||
},
|
||||
tsData := &qbv5.TimeSeriesData{QueryName: q.query.Name}
|
||||
// No bucket at all when nothing survived: a bucket holding no series reads
|
||||
// as "filtered to empty" to the cache, which stores it as a real result.
|
||||
if len(series) > 0 {
|
||||
tsData.Aggregations = []*qbv5.AggregationBucket{{Series: series}}
|
||||
}
|
||||
|
||||
var payload any = tsData
|
||||
|
||||
@@ -2,14 +2,21 @@ package querier
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus/prometheustest"
|
||||
qbv5 "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRemoveAllVarMatchers(t *testing.T) {
|
||||
@@ -453,3 +460,82 @@ func TestFingerprint_PinnedProviderBypassesCache(t *testing.T) {
|
||||
}
|
||||
assert.Empty(t, q.Fingerprint())
|
||||
}
|
||||
|
||||
func TestToResultDropsNonFiniteValues(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
floats []promql.FPoint
|
||||
expectedTimestamps []int64
|
||||
expectedValues []float64
|
||||
}{
|
||||
{
|
||||
description: "finite values pass through untouched",
|
||||
floats: []promql.FPoint{{T: 1000, F: 1.5}, {T: 2000, F: 2.5}},
|
||||
expectedTimestamps: []int64{1000, 2000},
|
||||
expectedValues: []float64{1.5, 2.5},
|
||||
},
|
||||
{
|
||||
description: "a ratio's 0/0 points are dropped, the rest kept",
|
||||
floats: []promql.FPoint{{T: 1000, F: 1.5}, {T: 2000, F: math.NaN()}, {T: 3000, F: 2.5}},
|
||||
expectedTimestamps: []int64{1000, 3000},
|
||||
expectedValues: []float64{1.5, 2.5},
|
||||
},
|
||||
{
|
||||
description: "both infinities are dropped",
|
||||
floats: []promql.FPoint{{T: 1000, F: math.Inf(1)}, {T: 2000, F: 4.5}, {T: 3000, F: math.Inf(-1)}},
|
||||
expectedTimestamps: []int64{2000},
|
||||
expectedValues: []float64{4.5},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
q := &promqlQuery{query: qbv5.PromQuery{Name: "A"}, requestType: qbv5.RequestTypeTimeSeries}
|
||||
matrix := promql.Matrix{{Metric: labels.FromStrings("job_name", "dbBloatMonitorJob"), Floats: test.floats}}
|
||||
|
||||
var mu sync.Mutex
|
||||
var rows, bytes uint64
|
||||
result := q.toResult(matrix, nil, time.Now(), &mu, &rows, &bytes)
|
||||
|
||||
tsData, ok := result.Value.(*qbv5.TimeSeriesData)
|
||||
require.True(t, ok)
|
||||
require.Len(t, tsData.Aggregations, 1)
|
||||
require.Len(t, tsData.Aggregations[0].Series, 1)
|
||||
|
||||
timestamps := make([]int64, 0, len(test.expectedTimestamps))
|
||||
values := make([]float64, 0, len(test.expectedValues))
|
||||
for _, v := range tsData.Aggregations[0].Series[0].Values {
|
||||
timestamps = append(timestamps, v.Timestamp)
|
||||
values = append(values, v.Value)
|
||||
}
|
||||
assert.Equal(t, test.expectedTimestamps, timestamps)
|
||||
assert.Equal(t, test.expectedValues, values)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// A series left with nothing must not surface as an empty series, and a result
|
||||
// left with no series must carry no aggregation bucket at all — the cache reads
|
||||
// a bucket holding no series as a real, filtered-to-empty result and stores it.
|
||||
func TestToResultDropsSeriesAndBucketLeftEmpty(t *testing.T) {
|
||||
q := &promqlQuery{query: qbv5.PromQuery{Name: "A"}, requestType: qbv5.RequestTypeTimeSeries}
|
||||
matrix := promql.Matrix{
|
||||
{Metric: labels.FromStrings("job_name", "idleJob"), Floats: []promql.FPoint{{T: 1000, F: math.NaN()}}},
|
||||
{Metric: labels.FromStrings("job_name", "activeJob"), Floats: []promql.FPoint{{T: 1000, F: 7.5}}},
|
||||
}
|
||||
|
||||
var mu sync.Mutex
|
||||
var rows, bytes uint64
|
||||
tsData, ok := q.toResult(matrix, nil, time.Now(), &mu, &rows, &bytes).Value.(*qbv5.TimeSeriesData)
|
||||
require.True(t, ok)
|
||||
require.Len(t, tsData.Aggregations, 1)
|
||||
require.Len(t, tsData.Aggregations[0].Series, 1, "the all-NaN series is gone")
|
||||
assert.Equal(t, "activeJob", tsData.Aggregations[0].Series[0].Labels[0].Value)
|
||||
|
||||
allNaN := promql.Matrix{
|
||||
{Metric: labels.FromStrings("job_name", "idleJob"), Floats: []promql.FPoint{{T: 1000, F: math.NaN()}}},
|
||||
}
|
||||
tsData, ok = q.toResult(allNaN, nil, time.Now(), &mu, &rows, &bytes).Value.(*qbv5.TimeSeriesData)
|
||||
require.True(t, ok)
|
||||
assert.Empty(t, tsData.Aggregations)
|
||||
}
|
||||
|
||||
@@ -53,12 +53,6 @@ var (
|
||||
types.RoleEditor: SigNozEditorRoleName,
|
||||
types.RoleViewer: SigNozViewerRoleName,
|
||||
}
|
||||
|
||||
SigNozManagedRoleToExistingLegacyRole = map[string]types.Role{
|
||||
SigNozAdminRoleName: types.RoleAdmin,
|
||||
SigNozEditorRoleName: types.RoleEditor,
|
||||
SigNozViewerRoleName: types.RoleViewer,
|
||||
}
|
||||
)
|
||||
|
||||
type Role struct {
|
||||
|
||||
@@ -119,9 +119,6 @@ type UserRoleStore interface {
|
||||
// get a single user role entry by org id and its own id
|
||||
GetUserRoleByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*UserRole, error)
|
||||
|
||||
// list all user_role entries for
|
||||
ListUserRolesByOrgIDAndUserIDs(ctx context.Context, orgID valuer.UUID, userIDs []valuer.UUID) ([]*UserRole, error)
|
||||
|
||||
// delete user role entries by user id
|
||||
DeleteUserRoles(ctx context.Context, userID valuer.UUID) error
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ var (
|
||||
GCPServiceComputeEngine = ServiceID{valuer.NewString("computeengine")}
|
||||
GCPServiceGKE = ServiceID{valuer.NewString("gke")}
|
||||
GCPServiceCloudStorage = ServiceID{valuer.NewString("cloudstorage")}
|
||||
GCPServiceCloudSQLMySQL = ServiceID{valuer.NewString("cloudsql_mysql")}
|
||||
)
|
||||
|
||||
func (ServiceID) Enum() []any {
|
||||
@@ -82,6 +83,7 @@ func (ServiceID) Enum() []any {
|
||||
GCPServiceComputeEngine,
|
||||
GCPServiceGKE,
|
||||
GCPServiceCloudStorage,
|
||||
GCPServiceCloudSQLMySQL,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +126,7 @@ var SupportedServices = map[CloudProviderType][]ServiceID{
|
||||
GCPServiceComputeEngine,
|
||||
GCPServiceGKE,
|
||||
GCPServiceCloudStorage,
|
||||
GCPServiceCloudSQLMySQL,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInviteAlreadyExists = errors.MustNewCode("invite_already_exists")
|
||||
ErrInviteNotFound = errors.MustNewCode("invite_not_found")
|
||||
)
|
||||
|
||||
type GettableInvite = Invite
|
||||
|
||||
type Invite struct {
|
||||
bun.BaseModel `bun:"table:user_invite"`
|
||||
|
||||
Identifiable
|
||||
TimeAuditable
|
||||
Name string `bun:"name,type:text" json:"name"`
|
||||
Email valuer.Email `bun:"email,type:text" json:"email"`
|
||||
Token string `bun:"token,type:text" json:"token"`
|
||||
Role Role `bun:"role,type:text" json:"role"`
|
||||
OrgID valuer.UUID `bun:"org_id,type:text" json:"orgId"`
|
||||
|
||||
InviteLink string `bun:"-" json:"inviteLink"`
|
||||
}
|
||||
|
||||
type PostableInvite struct {
|
||||
Name string `json:"name"`
|
||||
Email valuer.Email `json:"email"`
|
||||
Role Role `json:"role"`
|
||||
FrontendBaseUrl string `json:"frontendBaseUrl"`
|
||||
}
|
||||
|
||||
type PostableBulkInviteRequest struct {
|
||||
Invites []PostableInvite `json:"invites" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
func (request *PostableBulkInviteRequest) UnmarshalJSON(data []byte) error {
|
||||
type Alias PostableBulkInviteRequest
|
||||
|
||||
var temp Alias
|
||||
if err := json.Unmarshal(data, &temp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check for duplicate emails in the same request
|
||||
seen := make(map[string]struct{}, len(temp.Invites))
|
||||
for _, invite := range temp.Invites {
|
||||
email := invite.Email.StringValue()
|
||||
if _, exists := seen[email]; exists {
|
||||
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "Duplicate email in request: %s", email)
|
||||
}
|
||||
seen[email] = struct{}{}
|
||||
}
|
||||
|
||||
*request = PostableBulkInviteRequest(temp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewInvite(name string, role Role, orgID valuer.UUID, email valuer.Email) (*Invite, error) {
|
||||
invite := &Invite{
|
||||
Identifiable: Identifiable{
|
||||
ID: valuer.GenerateUUID(),
|
||||
},
|
||||
Name: name,
|
||||
Email: email,
|
||||
Token: valuer.GenerateUUID().String(),
|
||||
Role: role,
|
||||
OrgID: orgID,
|
||||
TimeAuditable: TimeAuditable{
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
return invite, nil
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
@@ -31,11 +32,12 @@ func (enum *Signal) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
var (
|
||||
SignalTraces = Signal{valuer.NewString("traces")}
|
||||
SignalLogs = Signal{valuer.NewString("logs")}
|
||||
SignalApiMonitoring = Signal{valuer.NewString("api_monitoring")}
|
||||
SignalExceptions = Signal{valuer.NewString("exceptions")}
|
||||
SignalMeter = Signal{valuer.NewString("meter")}
|
||||
SignalTraces = Signal{valuer.NewString("traces")}
|
||||
SignalLogs = Signal{valuer.NewString("logs")}
|
||||
SignalApiMonitoring = Signal{valuer.NewString("api_monitoring")}
|
||||
SignalExceptions = Signal{valuer.NewString("exceptions")}
|
||||
SignalMeter = Signal{valuer.NewString("meter")}
|
||||
SignalAiObservability = Signal{valuer.NewString("ai_observability")}
|
||||
)
|
||||
|
||||
// NewSignal creates a Signal from a string.
|
||||
@@ -51,6 +53,8 @@ func NewSignal(s string) (Signal, error) {
|
||||
return SignalExceptions, nil
|
||||
case "meter":
|
||||
return SignalMeter, nil
|
||||
case "ai_observability":
|
||||
return SignalAiObservability, nil
|
||||
default:
|
||||
return Signal{}, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid signal: %s", s)
|
||||
}
|
||||
@@ -187,6 +191,18 @@ func NewDefaultQuickFilter(orgID valuer.UUID) ([]*StorableQuickFilter, error) {
|
||||
{"key": "host.name", "dataType": "float64", "type": "Sum"},
|
||||
}
|
||||
|
||||
// AI observability (builder_ai_query trace explorer), ordered by expected
|
||||
// usage: env scoping, the LLM identity keys, then service and the rest.
|
||||
aiObservabilityFilters := []map[string]interface{}{
|
||||
{"key": "deployment.environment", "dataType": "string", "type": "resource"},
|
||||
{"key": telemetrytypes.GenAIOperationName, "dataType": "string", "type": "tag"},
|
||||
{"key": telemetrytypes.GenAIProviderName, "dataType": "string", "type": "tag"},
|
||||
{"key": telemetrytypes.GenAIRequestModel, "dataType": "string", "type": "tag"},
|
||||
{"key": "service.name", "dataType": "string", "type": "resource"},
|
||||
{"key": telemetrytypes.GenAIToolName, "dataType": "string", "type": "tag"},
|
||||
{"key": telemetrytypes.GenAIAgentName, "dataType": "string", "type": "tag"},
|
||||
}
|
||||
|
||||
tracesJSON, err := json.Marshal(tracesFilters)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to marshal traces filters")
|
||||
@@ -212,6 +228,11 @@ func NewDefaultQuickFilter(orgID valuer.UUID) ([]*StorableQuickFilter, error) {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to marshal meter filters")
|
||||
}
|
||||
|
||||
aiObservabilityJSON, err := json.Marshal(aiObservabilityFilters)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to marshal ai observability filters")
|
||||
}
|
||||
|
||||
timeRightNow := time.Now()
|
||||
|
||||
return []*StorableQuickFilter{
|
||||
@@ -275,5 +296,17 @@ func NewDefaultQuickFilter(orgID valuer.UUID) ([]*StorableQuickFilter, error) {
|
||||
UpdatedAt: timeRightNow,
|
||||
},
|
||||
},
|
||||
{
|
||||
Identifiable: types.Identifiable{
|
||||
ID: valuer.GenerateUUID(),
|
||||
},
|
||||
OrgID: orgID,
|
||||
Filter: string(aiObservabilityJSON),
|
||||
Signal: SignalAiObservability,
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: timeRightNow,
|
||||
UpdatedAt: timeRightNow,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ package telemetrytypes
|
||||
// OpenTelemetry gen_ai semantic-convention attribute keys. Single source of truth
|
||||
// shared by the AI query builder and the LLM pricing pipeline.
|
||||
const (
|
||||
GenAIRequestModel = "gen_ai.request.model"
|
||||
GenAIToolName = "gen_ai.tool.name"
|
||||
GenAIAgentName = "gen_ai.agent.name"
|
||||
GenAIProviderName = "gen_ai.provider.name"
|
||||
GenAIRequestModel = "gen_ai.request.model"
|
||||
GenAIOperationName = "gen_ai.operation.name"
|
||||
GenAIToolName = "gen_ai.tool.name"
|
||||
GenAIAgentName = "gen_ai.agent.name"
|
||||
GenAIProviderName = "gen_ai.provider.name"
|
||||
|
||||
GenAIUsageInputTokens = "gen_ai.usage.input_tokens"
|
||||
GenAIUsageOutputTokens = "gen_ai.usage.output_tokens"
|
||||
@@ -25,10 +26,11 @@ const (
|
||||
// on, surfaced by the metadata store even before ingestion so the AI gate/columns
|
||||
// resolve on a fresh install.
|
||||
var GenAIFieldDefinitions = map[string]TelemetryFieldKey{
|
||||
GenAIRequestModel: {Name: GenAIRequestModel, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeString},
|
||||
GenAIToolName: {Name: GenAIToolName, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeString},
|
||||
GenAIAgentName: {Name: GenAIAgentName, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeString},
|
||||
GenAIProviderName: {Name: GenAIProviderName, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeString},
|
||||
GenAIRequestModel: {Name: GenAIRequestModel, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeString},
|
||||
GenAIOperationName: {Name: GenAIOperationName, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeString},
|
||||
GenAIToolName: {Name: GenAIToolName, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeString},
|
||||
GenAIAgentName: {Name: GenAIAgentName, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeString},
|
||||
GenAIProviderName: {Name: GenAIProviderName, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeString},
|
||||
|
||||
GenAIUsageInputTokens: {Name: GenAIUsageInputTokens, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeFloat64},
|
||||
GenAIUsageOutputTokens: {Name: GenAIUsageOutputTokens, Signal: SignalTraces, FieldContext: FieldContextAttribute, FieldDataType: FieldDataTypeFloat64},
|
||||
|
||||
@@ -46,19 +46,10 @@ type User struct {
|
||||
TimeAuditable
|
||||
}
|
||||
|
||||
type DeprecatedUser struct {
|
||||
*User
|
||||
Role Role `json:"role"`
|
||||
}
|
||||
|
||||
type UpdatableUser struct {
|
||||
DisplayName string `json:"displayName" required:"true"`
|
||||
}
|
||||
|
||||
type PostableRole struct {
|
||||
Name string `json:"name" required:"true"`
|
||||
}
|
||||
|
||||
type PostableRegisterOrgAndAdmin struct {
|
||||
Name string `json:"name"`
|
||||
Email valuer.Email `json:"email"`
|
||||
@@ -121,13 +112,6 @@ func NewRootUser(displayName string, email valuer.Email, orgID valuer.UUID) (*Us
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewDeprecatedUserFromUserAndRole(user *User, role Role) *DeprecatedUser {
|
||||
return &DeprecatedUser{
|
||||
user,
|
||||
role,
|
||||
}
|
||||
}
|
||||
|
||||
// Update applies mutable fields from the input to the user. Immutable fields
|
||||
// (email, is_root, org_id, id) are preserved. Only non-zero input fields are applied.
|
||||
func (u *User) Update(displayName string) {
|
||||
|
||||
@@ -1,4 +1,116 @@
|
||||
{
|
||||
"note": "Divergences of the CURRENT promql serving path from the upstream reference engine, enforced exactly by 01_upstream_corpus.py in both directions. These document shipped defects, not test debt: the dominant class is the v1 remote-read fetch injecting a synthetic 'fingerprint' label into every series (pkg/prometheus/clickhouseprometheus/json.go), which breaks without() grouping and default vector matching. Entries must be REMOVED as the serving path is fixed or swapped.",
|
||||
"divergences": {}
|
||||
}
|
||||
"note": "Divergences of the CURRENT promql serving path from the upstream reference engine, enforced exactly by 01_upstream_corpus.py in both directions. These document shipped defects, not test debt: the dominant class is the v1 remote-read fetch injecting a synthetic 'fingerprint' label into every series (pkg/prometheus/clickhouseprometheus/json.go), which breaks without() grouping and default vector matching. Entries must be REMOVED as the serving path is fixed or swapped. Second class, and the bulk of the entries below: promql_query.go drops NaN and +/-Inf from results, mirroring the builder path in consume.go, so every case whose expected output carries a non-finite value diverges on both legs. That class is a product decision rather than a defect, so it is not part of the burn-down.",
|
||||
"divergences": {
|
||||
"aggregators.test:630[base]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:630[instant-coarse]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:633[base]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:633[instant-coarse]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:636[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:636[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:639[base]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:639[instant-coarse]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:642[base]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:642[instant-coarse]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:645[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:645[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:648[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:648[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:661[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:661[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:698[base]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:698[instant-coarse]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:702[base]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:702[instant-coarse]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:706[base]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:706[instant-coarse]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:710[base]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:710[instant-coarse]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:714[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:714[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:717[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:717[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:720[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:720[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:724[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:724[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:862[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:862[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:865[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:865[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:868[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:868[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:873[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:873[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:885[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:885[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:888[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:888[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:891[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:891[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:896[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:896[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:906[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:906[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:909[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:909[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:919[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:919[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:922[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:922[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:925[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:925[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:930[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:930[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:942[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:942[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:945[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:945[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:948[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:948[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:953[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:953[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:963[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:963[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:966[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:966[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"operators.test:533[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"operators.test:533[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"operators.test:539[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"trig_functions.test:13[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:13[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:18[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:18[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:23[base]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:23[instant-coarse]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:28[base]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:28[instant-coarse]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:33[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:33[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:38[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:38[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:43[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:43[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:48[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:48[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:53[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:53[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:58[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:58[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:63[base]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:63[instant-coarse]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:68[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:68[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:73[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:73[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:78[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:78[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:83[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:83[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:88[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:88[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:8[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:8[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:93[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:93[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,128 @@
|
||||
{
|
||||
"note": "Divergences of the clickhousev2 provider (pinned via X-SigNoz-PromQL-Provider) from the upstream reference engine, enforced exactly by 01_upstream_corpus.py in both directions. This ledger is the rollout scorecard for the provider swap: the default provider cannot be replaced by clickhousev2 while anything is listed here. Entries must carry the defect's cause and be REMOVED as the provider is fixed. Current class: the engine aggregates floats with Kahan compensated summation (sum, sum_over_time) and an overflow-free incremental mean (avg); ClickHouse's sumForEach/avgForEach/arraySum are naive, so extreme-magnitude corpus data (±1e100 cancellation, ±1.8e308 overflow) diverges on transpiled plans. Burn-down candidates: sumKahanForEach for the cancellation class; the overflow class needs an incremental-mean aggregate ClickHouse does not have.",
|
||||
"note": "Divergences of the clickhousev2 provider (pinned via X-SigNoz-PromQL-Provider) from the upstream reference engine, enforced exactly by 01_upstream_corpus.py in both directions. This ledger is the rollout scorecard for the provider swap: the default provider cannot be replaced by clickhousev2 while anything is listed here. Entries must carry the defect's cause and be REMOVED as the provider is fixed. Current class: the engine aggregates floats with Kahan compensated summation (sum, sum_over_time) and an overflow-free incremental mean (avg); ClickHouse's sumForEach/avgForEach/arraySum are naive, so extreme-magnitude corpus data (±1e100 cancellation, ±1.8e308 overflow) diverges on transpiled plans. Burn-down candidates: sumKahanForEach for the cancellation class; the overflow class needs an incremental-mean aggregate ClickHouse does not have. Second class, and the bulk of the entries below: promql_query.go drops NaN and +/-Inf from results, mirroring the builder path in consume.go, so every case whose expected output carries a non-finite value diverges on both legs. That class is a product decision rather than a defect, so it is not part of the burn-down.",
|
||||
"divergences": {
|
||||
"aggregators.test:630[base]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:630[instant-coarse]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:633[base]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:633[instant-coarse]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:636[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:636[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:639[base]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:639[instant-coarse]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:642[base]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:642[instant-coarse]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:645[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:645[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:648[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:648[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:651[base]": "avg over near-max-float64 values: engine's incremental mean never forms the overflowing sum; avgForEach sums then divides, overflowing to +Inf",
|
||||
"aggregators.test:651[instant-coarse]": "same as aggregators.test:651[base] on the coarse-step grid variant",
|
||||
"aggregators.test:654[base]": "avg over near-min-float64 values: engine's incremental mean never forms the overflowing sum; avgForEach overflows to -Inf",
|
||||
"aggregators.test:654[instant-coarse]": "same as aggregators.test:654[base] on the coarse-step grid variant",
|
||||
"aggregators.test:661[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:661[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:687[base]": "sum over {1e100, -1e100, small}: engine uses Kahan compensated summation; sumForEach's naive summation loses the small terms to cancellation and returns 0",
|
||||
"aggregators.test:687[instant-coarse]": "same as aggregators.test:687[base] on the coarse-step grid variant",
|
||||
"aggregators.test:695[base]": "avg over {1e100, -1e100, small}: same Kahan-vs-naive cancellation as aggregators.test:687, divided by count",
|
||||
"aggregators.test:695[instant-coarse]": "same as aggregators.test:695[base] on the coarse-step grid variant",
|
||||
"aggregators.test:698[base]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:698[instant-coarse]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:702[base]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:702[instant-coarse]": "expected output is entirely Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:706[base]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:706[instant-coarse]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:710[base]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:710[instant-coarse]": "expected output is entirely -Inf; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:714[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:714[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:717[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:717[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:720[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:720[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:724[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:724[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:862[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:862[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:865[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:865[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:868[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:868[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:873[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:873[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:885[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:885[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:888[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:888[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:891[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:891[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:896[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:896[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:906[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:906[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:909[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:909[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:919[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:919[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:922[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:922[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:925[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:925[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:930[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:930[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:942[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:942[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:945[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:945[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:948[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:948[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:953[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:953[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"aggregators.test:963[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:963[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:966[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"aggregators.test:966[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"functions.test:1084[instant-coarse]": "sum_over_time over a window containing ±1e100: the disjoint coarse-step form's arraySum slide is naive summation, cancelling to 0 (the base variant's W>64 shape falls back to the engine and is exact)",
|
||||
"functions.test:1087[instant-coarse]": "avg_over_time, same window and cancellation as functions.test:1084[instant-coarse]",
|
||||
"functions.test:1149[base]": "avg_over_time over ±2.258e220-magnitude samples: engine's Kahan-compensated incremental mean cancels exactly to 0; the bucketed form's naive slide summation leaves a ~1e202 residue",
|
||||
"functions.test:1149[instant-coarse]": "same as functions.test:1149[base] through the disjoint coarse-step form"
|
||||
"functions.test:1149[instant-coarse]": "same as functions.test:1149[base] through the disjoint coarse-step form",
|
||||
"operators.test:533[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"operators.test:533[instant-coarse]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"operators.test:539[base]": "expected output is entirely NaN; promql_query.go drops non-finite values, so no series is emitted",
|
||||
"trig_functions.test:13[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:13[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:18[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:18[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:23[base]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:23[instant-coarse]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:28[base]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:28[instant-coarse]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:33[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:33[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:38[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:38[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:43[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:43[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:48[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:48[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:53[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:53[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:58[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:58[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:63[base]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:63[instant-coarse]": "2 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:68[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:68[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:73[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:73[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:78[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:78[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:83[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:83[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:88[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:88[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:8[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:8[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:93[base]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing",
|
||||
"trig_functions.test:93[instant-coarse]": "1 of 3 expected points are NaN; promql_query.go drops non-finite values, so those timestamps are missing"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
from collections.abc import Callable
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from http import HTTPStatus
|
||||
from uuid import uuid4
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD
|
||||
from fixtures.metrics import Metrics
|
||||
from fixtures.querier import get_all_series, make_query_request
|
||||
|
||||
HOUR_MS = 3_600_000
|
||||
SAMPLE_INTERVAL_MS = 60_000
|
||||
|
||||
|
||||
def test_promql_ratio_with_zero_denominator_is_dropped_and_cached(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
insert_metrics: Callable[[list[Metrics]], None],
|
||||
) -> None:
|
||||
# 12h ending on an hour boundary 15m ago — old enough to be cached.
|
||||
end_ms = (int((datetime.now(tz=UTC) - timedelta(minutes=15)).timestamp() * 1000) // HOUR_MS) * HOUR_MS
|
||||
start_ms = end_ms - 12 * HOUR_MS
|
||||
|
||||
sum_metric = f"job_duration_sum_{uuid4().hex[:8]}"
|
||||
count_metric = f"job_duration_count_{uuid4().hex[:8]}"
|
||||
|
||||
# active_job divides finite; idle_job is 0/0 at every step.
|
||||
series = {"active_job": (100.0, 4.0), "idle_job": (0.0, 0.0)}
|
||||
metrics: list[Metrics] = []
|
||||
for job_name, (sum_value, count_value) in series.items():
|
||||
for ts_ms in range(start_ms, end_ms + 1, SAMPLE_INTERVAL_MS):
|
||||
timestamp = datetime.fromtimestamp(ts_ms / 1000, tz=UTC)
|
||||
metrics.append(Metrics(metric_name=sum_metric, labels={"job_name": job_name}, timestamp=timestamp, value=sum_value))
|
||||
metrics.append(Metrics(metric_name=count_metric, labels={"job_name": job_name}, timestamp=timestamp, value=count_value))
|
||||
insert_metrics(metrics)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
promql = f"sum by (job_name) ({sum_metric}) / sum by (job_name) ({count_metric})"
|
||||
|
||||
def run() -> tuple[dict[str, dict[int, object]], int]:
|
||||
query = {"type": "promql", "spec": {"name": "A", "query": promql}}
|
||||
response = make_query_request(signoz, token, start_ms, end_ms, [query], no_cache=False)
|
||||
assert response.status_code == HTTPStatus.OK, response.text[:300]
|
||||
body = response.json()
|
||||
out: dict[str, dict[int, object]] = {}
|
||||
for entry in get_all_series(body, "A") or []:
|
||||
labels = {l["key"]["name"]: str(l["value"]) for l in entry.get("labels") or []}
|
||||
out[labels["job_name"]] = {v["timestamp"]: v["value"] for v in entry.get("values") or []}
|
||||
return out, int(body["data"]["meta"]["stepIntervals"]["A"])
|
||||
|
||||
# First populates the cache, second must be served from it.
|
||||
first, step_seconds = run()
|
||||
second, _ = run()
|
||||
|
||||
expected_points = (end_ms - start_ms) // (step_seconds * 1000) + 1
|
||||
assert set(first) == {"active_job"}, f"the 0/0 series must not reach the response: {sorted(first)}"
|
||||
assert set(first["active_job"].values()) == {25.0}, sorted(set(first["active_job"].values()))
|
||||
assert len(first["active_job"]) == expected_points, f"expected {expected_points} points, got {len(first['active_job'])}"
|
||||
|
||||
# The cached read excludes end_ms, the one legitimate difference.
|
||||
assert set(second) == set(first), sorted(second)
|
||||
for job_name, points in first.items():
|
||||
expected = {ts: value for ts, value in points.items() if ts < end_ms}
|
||||
assert second[job_name] == expected, f"{job_name}: got {len(second[job_name])} of {len(expected)} points"
|
||||
Reference in New Issue
Block a user