mirror of
https://github.com/SigNoz/signoz.git
synced 2026-03-03 04:22:03 +00:00
Compare commits
10 Commits
chore/uplo
...
refactor/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a09dc325de | ||
|
|
37cd1ab84b | ||
|
|
379b4f7fc4 | ||
|
|
5e536ae077 | ||
|
|
234585e642 | ||
|
|
2cc14f1ad4 | ||
|
|
dc4ed4d239 | ||
|
|
8184e60c03 | ||
|
|
7281c36873 | ||
|
|
40288776e8 |
@@ -1763,6 +1763,134 @@ components:
|
|||||||
- type
|
- type
|
||||||
- orgId
|
- orgId
|
||||||
type: object
|
type: object
|
||||||
|
ServiceaccounttypesFactorAPIKey:
|
||||||
|
properties:
|
||||||
|
createdAt:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
expires_at:
|
||||||
|
minimum: 0
|
||||||
|
type: integer
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
last_used:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
service_account_id:
|
||||||
|
type: string
|
||||||
|
updatedAt:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- key
|
||||||
|
- expires_at
|
||||||
|
- last_used
|
||||||
|
- service_account_id
|
||||||
|
type: object
|
||||||
|
ServiceaccounttypesGettableFactorAPIKeyWithKey:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
ServiceaccounttypesPostableFactorAPIKey:
|
||||||
|
properties:
|
||||||
|
expires_at:
|
||||||
|
minimum: 0
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- expires_at
|
||||||
|
type: object
|
||||||
|
ServiceaccounttypesPostableServiceAccount:
|
||||||
|
properties:
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
roles:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- email
|
||||||
|
- roles
|
||||||
|
type: object
|
||||||
|
ServiceaccounttypesServiceAccount:
|
||||||
|
properties:
|
||||||
|
createdAt:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
orgID:
|
||||||
|
type: string
|
||||||
|
roles:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
updatedAt:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- email
|
||||||
|
- roles
|
||||||
|
- status
|
||||||
|
- orgID
|
||||||
|
type: object
|
||||||
|
ServiceaccounttypesUpdatableFactorAPIKey:
|
||||||
|
properties:
|
||||||
|
expires_at:
|
||||||
|
minimum: 0
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- expires_at
|
||||||
|
type: object
|
||||||
|
ServiceaccounttypesUpdatableServiceAccount:
|
||||||
|
properties:
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
roles:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- email
|
||||||
|
- roles
|
||||||
|
type: object
|
||||||
|
ServiceaccounttypesUpdatableServiceAccountStatus:
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
type: object
|
||||||
TelemetrytypesFieldContext:
|
TelemetrytypesFieldContext:
|
||||||
enum:
|
enum:
|
||||||
- metric
|
- metric
|
||||||
@@ -4537,6 +4665,586 @@ paths:
|
|||||||
summary: Patch objects for a role by relation
|
summary: Patch objects for a role by relation
|
||||||
tags:
|
tags:
|
||||||
- role
|
- role
|
||||||
|
/api/v1/service_accounts:
|
||||||
|
get:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint lists the service accounts for an organisation
|
||||||
|
operationId: ListServiceAccounts
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/ServiceaccounttypesServiceAccount'
|
||||||
|
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 service accounts
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
|
post:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint creates a service account
|
||||||
|
operationId: CreateServiceAccount
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ServiceaccounttypesPostableServiceAccount'
|
||||||
|
responses:
|
||||||
|
"201":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/TypesIdentifiable'
|
||||||
|
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 service account
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
|
/api/v1/service_accounts/{id}:
|
||||||
|
delete:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint deletes an existing service account
|
||||||
|
operationId: DeleteServiceAccount
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
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: Deletes a service account
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
|
get:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint gets an existing service account
|
||||||
|
operationId: GetServiceAccount
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/ServiceaccounttypesServiceAccount'
|
||||||
|
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
|
||||||
|
"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: Gets a service account
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
|
put:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint updates an existing service account
|
||||||
|
operationId: UpdateServiceAccount
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ServiceaccounttypesUpdatableServiceAccount'
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: No Content
|
||||||
|
"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: Updates a service account
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
|
/api/v1/service_accounts/{id}/keys:
|
||||||
|
get:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint lists the service account keys
|
||||||
|
operationId: ListServiceAccountKeys
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/ServiceaccounttypesFactorAPIKey'
|
||||||
|
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 service account keys
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
|
post:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint creates a service account key
|
||||||
|
operationId: CreateServiceAccountKey
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ServiceaccounttypesPostableFactorAPIKey'
|
||||||
|
responses:
|
||||||
|
"201":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/ServiceaccounttypesGettableFactorAPIKeyWithKey'
|
||||||
|
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 a service account key
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
|
/api/v1/service_accounts/{id}/keys/{fid}:
|
||||||
|
delete:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint revokes an existing service account key
|
||||||
|
operationId: RevokeServiceAccountKey
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: path
|
||||||
|
name: fid
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
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: Revoke a service account key
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
|
put:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint updates an existing service account key
|
||||||
|
operationId: UpdateServiceAccountKey
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: path
|
||||||
|
name: fid
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ServiceaccounttypesUpdatableFactorAPIKey'
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: No Content
|
||||||
|
"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: Updates a service account key
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
|
/api/v1/service_accounts/{id}/status:
|
||||||
|
put:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint updates an existing service account status
|
||||||
|
operationId: UpdateServiceAccountStatus
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ServiceaccounttypesUpdatableServiceAccountStatus'
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: No Content
|
||||||
|
"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: Updates a service account status
|
||||||
|
tags:
|
||||||
|
- serviceaccount
|
||||||
/api/v1/user:
|
/api/v1/user:
|
||||||
get:
|
get:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
|
|||||||
@@ -98,16 +98,20 @@ func (provider *provider) ListByOrgIDAndNames(ctx context.Context, orgID valuer.
|
|||||||
return provider.pkgAuthzService.ListByOrgIDAndNames(ctx, orgID, names)
|
return provider.pkgAuthzService.ListByOrgIDAndNames(ctx, orgID, names)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Grant(ctx context.Context, orgID valuer.UUID, name string, subject string) error {
|
func (provider *provider) ListByOrgIDAndIDs(ctx context.Context, orgID valuer.UUID, ids []valuer.UUID) ([]*roletypes.Role, error) {
|
||||||
return provider.pkgAuthzService.Grant(ctx, orgID, name, subject)
|
return provider.pkgAuthzService.ListByOrgIDAndIDs(ctx, orgID, ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) ModifyGrant(ctx context.Context, orgID valuer.UUID, existingRoleName string, updatedRoleName string, subject string) error {
|
func (provider *provider) Grant(ctx context.Context, orgID valuer.UUID, names []string, subject string) error {
|
||||||
return provider.pkgAuthzService.ModifyGrant(ctx, orgID, existingRoleName, updatedRoleName, subject)
|
return provider.pkgAuthzService.Grant(ctx, orgID, names, subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Revoke(ctx context.Context, orgID valuer.UUID, name string, subject string) error {
|
func (provider *provider) ModifyGrant(ctx context.Context, orgID valuer.UUID, existingRoleNames []string, updatedRoleNames []string, subject string) error {
|
||||||
return provider.pkgAuthzService.Revoke(ctx, orgID, name, subject)
|
return provider.pkgAuthzService.ModifyGrant(ctx, orgID, existingRoleNames, updatedRoleNames, subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Revoke(ctx context.Context, orgID valuer.UUID, names []string, subject string) error {
|
||||||
|
return provider.pkgAuthzService.Revoke(ctx, orgID, names, subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) CreateManagedRoles(ctx context.Context, orgID valuer.UUID, managedRoles []*roletypes.Role) error {
|
func (provider *provider) CreateManagedRoles(ctx context.Context, orgID valuer.UUID, managedRoles []*roletypes.Role) error {
|
||||||
|
|||||||
973
frontend/src/api/generated/services/serviceaccount/index.ts
Normal file
973
frontend/src/api/generated/services/serviceaccount/index.ts
Normal file
@@ -0,0 +1,973 @@
|
|||||||
|
/**
|
||||||
|
* ! Do not edit manually
|
||||||
|
* * The file has been auto-generated using Orval for SigNoz
|
||||||
|
* * regenerate with 'yarn generate:api'
|
||||||
|
* SigNoz
|
||||||
|
*/
|
||||||
|
import type {
|
||||||
|
InvalidateOptions,
|
||||||
|
MutationFunction,
|
||||||
|
QueryClient,
|
||||||
|
QueryFunction,
|
||||||
|
QueryKey,
|
||||||
|
UseMutationOptions,
|
||||||
|
UseMutationResult,
|
||||||
|
UseQueryOptions,
|
||||||
|
UseQueryResult,
|
||||||
|
} from 'react-query';
|
||||||
|
import { useMutation, useQuery } from 'react-query';
|
||||||
|
|
||||||
|
import type { BodyType, ErrorType } from '../../../generatedAPIInstance';
|
||||||
|
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||||
|
import type {
|
||||||
|
CreateServiceAccount201,
|
||||||
|
CreateServiceAccountKey201,
|
||||||
|
CreateServiceAccountKeyPathParameters,
|
||||||
|
DeleteServiceAccountPathParameters,
|
||||||
|
GetServiceAccount200,
|
||||||
|
GetServiceAccountPathParameters,
|
||||||
|
ListServiceAccountKeys200,
|
||||||
|
ListServiceAccountKeysPathParameters,
|
||||||
|
ListServiceAccounts200,
|
||||||
|
RenderErrorResponseDTO,
|
||||||
|
RevokeServiceAccountKeyPathParameters,
|
||||||
|
ServiceaccounttypesPostableFactorAPIKeyDTO,
|
||||||
|
ServiceaccounttypesPostableServiceAccountDTO,
|
||||||
|
ServiceaccounttypesUpdatableFactorAPIKeyDTO,
|
||||||
|
ServiceaccounttypesUpdatableServiceAccountDTO,
|
||||||
|
ServiceaccounttypesUpdatableServiceAccountStatusDTO,
|
||||||
|
UpdateServiceAccountKeyPathParameters,
|
||||||
|
UpdateServiceAccountPathParameters,
|
||||||
|
UpdateServiceAccountStatusPathParameters,
|
||||||
|
} from '../sigNoz.schemas';
|
||||||
|
|
||||||
|
type AwaitedInput<T> = PromiseLike<T> | T;
|
||||||
|
|
||||||
|
type Awaited<O> = O extends AwaitedInput<infer T> ? T : never;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This endpoint lists the service accounts for an organisation
|
||||||
|
* @summary List service accounts
|
||||||
|
*/
|
||||||
|
export const listServiceAccounts = (signal?: AbortSignal) => {
|
||||||
|
return GeneratedAPIInstance<ListServiceAccounts200>({
|
||||||
|
url: `/api/v1/service_accounts`,
|
||||||
|
method: 'GET',
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getListServiceAccountsQueryKey = () => {
|
||||||
|
return [`/api/v1/service_accounts`] as const;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getListServiceAccountsQueryOptions = <
|
||||||
|
TData = Awaited<ReturnType<typeof listServiceAccounts>>,
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>
|
||||||
|
>(options?: {
|
||||||
|
query?: UseQueryOptions<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccounts>>,
|
||||||
|
TError,
|
||||||
|
TData
|
||||||
|
>;
|
||||||
|
}) => {
|
||||||
|
const { query: queryOptions } = options ?? {};
|
||||||
|
|
||||||
|
const queryKey = queryOptions?.queryKey ?? getListServiceAccountsQueryKey();
|
||||||
|
|
||||||
|
const queryFn: QueryFunction<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccounts>>
|
||||||
|
> = ({ signal }) => listServiceAccounts(signal);
|
||||||
|
|
||||||
|
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccounts>>,
|
||||||
|
TError,
|
||||||
|
TData
|
||||||
|
> & { queryKey: QueryKey };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ListServiceAccountsQueryResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccounts>>
|
||||||
|
>;
|
||||||
|
export type ListServiceAccountsQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary List service accounts
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function useListServiceAccounts<
|
||||||
|
TData = Awaited<ReturnType<typeof listServiceAccounts>>,
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>
|
||||||
|
>(options?: {
|
||||||
|
query?: UseQueryOptions<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccounts>>,
|
||||||
|
TError,
|
||||||
|
TData
|
||||||
|
>;
|
||||||
|
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||||
|
const queryOptions = getListServiceAccountsQueryOptions(options);
|
||||||
|
|
||||||
|
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||||
|
queryKey: QueryKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
query.queryKey = queryOptions.queryKey;
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary List service accounts
|
||||||
|
*/
|
||||||
|
export const invalidateListServiceAccounts = async (
|
||||||
|
queryClient: QueryClient,
|
||||||
|
options?: InvalidateOptions,
|
||||||
|
): Promise<QueryClient> => {
|
||||||
|
await queryClient.invalidateQueries(
|
||||||
|
{ queryKey: getListServiceAccountsQueryKey() },
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
return queryClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This endpoint creates a service account
|
||||||
|
* @summary Create service account
|
||||||
|
*/
|
||||||
|
export const createServiceAccount = (
|
||||||
|
serviceaccounttypesPostableServiceAccountDTO: BodyType<ServiceaccounttypesPostableServiceAccountDTO>,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
) => {
|
||||||
|
return GeneratedAPIInstance<CreateServiceAccount201>({
|
||||||
|
url: `/api/v1/service_accounts`,
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
data: serviceaccounttypesPostableServiceAccountDTO,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCreateServiceAccountMutationOptions = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> },
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> },
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationKey = ['createServiceAccount'];
|
||||||
|
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 createServiceAccount>>,
|
||||||
|
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> }
|
||||||
|
> = (props) => {
|
||||||
|
const { data } = props ?? {};
|
||||||
|
|
||||||
|
return createServiceAccount(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreateServiceAccountMutationResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccount>>
|
||||||
|
>;
|
||||||
|
export type CreateServiceAccountMutationBody = BodyType<ServiceaccounttypesPostableServiceAccountDTO>;
|
||||||
|
export type CreateServiceAccountMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Create service account
|
||||||
|
*/
|
||||||
|
export const useCreateServiceAccount = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> },
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{ data: BodyType<ServiceaccounttypesPostableServiceAccountDTO> },
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationOptions = getCreateServiceAccountMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* This endpoint deletes an existing service account
|
||||||
|
* @summary Deletes a service account
|
||||||
|
*/
|
||||||
|
export const deleteServiceAccount = ({
|
||||||
|
id,
|
||||||
|
}: DeleteServiceAccountPathParameters) => {
|
||||||
|
return GeneratedAPIInstance<string>({
|
||||||
|
url: `/api/v1/service_accounts/${id}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDeleteServiceAccountMutationOptions = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof deleteServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{ pathParams: DeleteServiceAccountPathParameters },
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof deleteServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{ pathParams: DeleteServiceAccountPathParameters },
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationKey = ['deleteServiceAccount'];
|
||||||
|
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 deleteServiceAccount>>,
|
||||||
|
{ pathParams: DeleteServiceAccountPathParameters }
|
||||||
|
> = (props) => {
|
||||||
|
const { pathParams } = props ?? {};
|
||||||
|
|
||||||
|
return deleteServiceAccount(pathParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DeleteServiceAccountMutationResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof deleteServiceAccount>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type DeleteServiceAccountMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Deletes a service account
|
||||||
|
*/
|
||||||
|
export const useDeleteServiceAccount = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof deleteServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{ pathParams: DeleteServiceAccountPathParameters },
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof deleteServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{ pathParams: DeleteServiceAccountPathParameters },
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationOptions = getDeleteServiceAccountMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* This endpoint gets an existing service account
|
||||||
|
* @summary Gets a service account
|
||||||
|
*/
|
||||||
|
export const getServiceAccount = (
|
||||||
|
{ id }: GetServiceAccountPathParameters,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
) => {
|
||||||
|
return GeneratedAPIInstance<GetServiceAccount200>({
|
||||||
|
url: `/api/v1/service_accounts/${id}`,
|
||||||
|
method: 'GET',
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGetServiceAccountQueryKey = ({
|
||||||
|
id,
|
||||||
|
}: GetServiceAccountPathParameters) => {
|
||||||
|
return [`/api/v1/service_accounts/${id}`] as const;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGetServiceAccountQueryOptions = <
|
||||||
|
TData = Awaited<ReturnType<typeof getServiceAccount>>,
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>
|
||||||
|
>(
|
||||||
|
{ id }: GetServiceAccountPathParameters,
|
||||||
|
options?: {
|
||||||
|
query?: UseQueryOptions<
|
||||||
|
Awaited<ReturnType<typeof getServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
TData
|
||||||
|
>;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { query: queryOptions } = options ?? {};
|
||||||
|
|
||||||
|
const queryKey =
|
||||||
|
queryOptions?.queryKey ?? getGetServiceAccountQueryKey({ id });
|
||||||
|
|
||||||
|
const queryFn: QueryFunction<
|
||||||
|
Awaited<ReturnType<typeof getServiceAccount>>
|
||||||
|
> = ({ signal }) => getServiceAccount({ id }, signal);
|
||||||
|
|
||||||
|
return {
|
||||||
|
queryKey,
|
||||||
|
queryFn,
|
||||||
|
enabled: !!id,
|
||||||
|
...queryOptions,
|
||||||
|
} as UseQueryOptions<
|
||||||
|
Awaited<ReturnType<typeof getServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
TData
|
||||||
|
> & { queryKey: QueryKey };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetServiceAccountQueryResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof getServiceAccount>>
|
||||||
|
>;
|
||||||
|
export type GetServiceAccountQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Gets a service account
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function useGetServiceAccount<
|
||||||
|
TData = Awaited<ReturnType<typeof getServiceAccount>>,
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>
|
||||||
|
>(
|
||||||
|
{ id }: GetServiceAccountPathParameters,
|
||||||
|
options?: {
|
||||||
|
query?: UseQueryOptions<
|
||||||
|
Awaited<ReturnType<typeof getServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
TData
|
||||||
|
>;
|
||||||
|
},
|
||||||
|
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||||
|
const queryOptions = getGetServiceAccountQueryOptions({ id }, options);
|
||||||
|
|
||||||
|
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||||
|
queryKey: QueryKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
query.queryKey = queryOptions.queryKey;
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Gets a service account
|
||||||
|
*/
|
||||||
|
export const invalidateGetServiceAccount = async (
|
||||||
|
queryClient: QueryClient,
|
||||||
|
{ id }: GetServiceAccountPathParameters,
|
||||||
|
options?: InvalidateOptions,
|
||||||
|
): Promise<QueryClient> => {
|
||||||
|
await queryClient.invalidateQueries(
|
||||||
|
{ queryKey: getGetServiceAccountQueryKey({ id }) },
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
return queryClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This endpoint updates an existing service account
|
||||||
|
* @summary Updates a service account
|
||||||
|
*/
|
||||||
|
export const updateServiceAccount = (
|
||||||
|
{ id }: UpdateServiceAccountPathParameters,
|
||||||
|
serviceaccounttypesUpdatableServiceAccountDTO: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>,
|
||||||
|
) => {
|
||||||
|
return GeneratedAPIInstance<string>({
|
||||||
|
url: `/api/v1/service_accounts/${id}`,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
data: serviceaccounttypesUpdatableServiceAccountDTO,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUpdateServiceAccountMutationOptions = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationKey = ['updateServiceAccount'];
|
||||||
|
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 updateServiceAccount>>,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
|
const { pathParams, data } = props ?? {};
|
||||||
|
|
||||||
|
return updateServiceAccount(pathParams, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateServiceAccountMutationResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccount>>
|
||||||
|
>;
|
||||||
|
export type UpdateServiceAccountMutationBody = BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
|
||||||
|
export type UpdateServiceAccountMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Updates a service account
|
||||||
|
*/
|
||||||
|
export const useUpdateServiceAccount = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccount>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationOptions = getUpdateServiceAccountMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* This endpoint lists the service account keys
|
||||||
|
* @summary List service account keys
|
||||||
|
*/
|
||||||
|
export const listServiceAccountKeys = (
|
||||||
|
{ id }: ListServiceAccountKeysPathParameters,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
) => {
|
||||||
|
return GeneratedAPIInstance<ListServiceAccountKeys200>({
|
||||||
|
url: `/api/v1/service_accounts/${id}/keys`,
|
||||||
|
method: 'GET',
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getListServiceAccountKeysQueryKey = ({
|
||||||
|
id,
|
||||||
|
}: ListServiceAccountKeysPathParameters) => {
|
||||||
|
return [`/api/v1/service_accounts/${id}/keys`] as const;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getListServiceAccountKeysQueryOptions = <
|
||||||
|
TData = Awaited<ReturnType<typeof listServiceAccountKeys>>,
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>
|
||||||
|
>(
|
||||||
|
{ id }: ListServiceAccountKeysPathParameters,
|
||||||
|
options?: {
|
||||||
|
query?: UseQueryOptions<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccountKeys>>,
|
||||||
|
TError,
|
||||||
|
TData
|
||||||
|
>;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { query: queryOptions } = options ?? {};
|
||||||
|
|
||||||
|
const queryKey =
|
||||||
|
queryOptions?.queryKey ?? getListServiceAccountKeysQueryKey({ id });
|
||||||
|
|
||||||
|
const queryFn: QueryFunction<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccountKeys>>
|
||||||
|
> = ({ signal }) => listServiceAccountKeys({ id }, signal);
|
||||||
|
|
||||||
|
return {
|
||||||
|
queryKey,
|
||||||
|
queryFn,
|
||||||
|
enabled: !!id,
|
||||||
|
...queryOptions,
|
||||||
|
} as UseQueryOptions<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccountKeys>>,
|
||||||
|
TError,
|
||||||
|
TData
|
||||||
|
> & { queryKey: QueryKey };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ListServiceAccountKeysQueryResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccountKeys>>
|
||||||
|
>;
|
||||||
|
export type ListServiceAccountKeysQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary List service account keys
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function useListServiceAccountKeys<
|
||||||
|
TData = Awaited<ReturnType<typeof listServiceAccountKeys>>,
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>
|
||||||
|
>(
|
||||||
|
{ id }: ListServiceAccountKeysPathParameters,
|
||||||
|
options?: {
|
||||||
|
query?: UseQueryOptions<
|
||||||
|
Awaited<ReturnType<typeof listServiceAccountKeys>>,
|
||||||
|
TError,
|
||||||
|
TData
|
||||||
|
>;
|
||||||
|
},
|
||||||
|
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||||
|
const queryOptions = getListServiceAccountKeysQueryOptions({ id }, options);
|
||||||
|
|
||||||
|
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||||
|
queryKey: QueryKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
query.queryKey = queryOptions.queryKey;
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary List service account keys
|
||||||
|
*/
|
||||||
|
export const invalidateListServiceAccountKeys = async (
|
||||||
|
queryClient: QueryClient,
|
||||||
|
{ id }: ListServiceAccountKeysPathParameters,
|
||||||
|
options?: InvalidateOptions,
|
||||||
|
): Promise<QueryClient> => {
|
||||||
|
await queryClient.invalidateQueries(
|
||||||
|
{ queryKey: getListServiceAccountKeysQueryKey({ id }) },
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
return queryClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This endpoint creates a service account key
|
||||||
|
* @summary Create a service account key
|
||||||
|
*/
|
||||||
|
export const createServiceAccountKey = (
|
||||||
|
{ id }: CreateServiceAccountKeyPathParameters,
|
||||||
|
serviceaccounttypesPostableFactorAPIKeyDTO: BodyType<ServiceaccounttypesPostableFactorAPIKeyDTO>,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
) => {
|
||||||
|
return GeneratedAPIInstance<CreateServiceAccountKey201>({
|
||||||
|
url: `/api/v1/service_accounts/${id}/keys`,
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
data: serviceaccounttypesPostableFactorAPIKeyDTO,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCreateServiceAccountKeyMutationOptions = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: CreateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesPostableFactorAPIKeyDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: CreateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesPostableFactorAPIKeyDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationKey = ['createServiceAccountKey'];
|
||||||
|
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 createServiceAccountKey>>,
|
||||||
|
{
|
||||||
|
pathParams: CreateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesPostableFactorAPIKeyDTO>;
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
|
const { pathParams, data } = props ?? {};
|
||||||
|
|
||||||
|
return createServiceAccountKey(pathParams, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreateServiceAccountKeyMutationResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccountKey>>
|
||||||
|
>;
|
||||||
|
export type CreateServiceAccountKeyMutationBody = BodyType<ServiceaccounttypesPostableFactorAPIKeyDTO>;
|
||||||
|
export type CreateServiceAccountKeyMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Create a service account key
|
||||||
|
*/
|
||||||
|
export const useCreateServiceAccountKey = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: CreateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesPostableFactorAPIKeyDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof createServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: CreateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesPostableFactorAPIKeyDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationOptions = getCreateServiceAccountKeyMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* This endpoint revokes an existing service account key
|
||||||
|
* @summary Revoke a service account key
|
||||||
|
*/
|
||||||
|
export const revokeServiceAccountKey = ({
|
||||||
|
id,
|
||||||
|
fid,
|
||||||
|
}: RevokeServiceAccountKeyPathParameters) => {
|
||||||
|
return GeneratedAPIInstance<string>({
|
||||||
|
url: `/api/v1/service_accounts/${id}/keys/${fid}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRevokeServiceAccountKeyMutationOptions = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof revokeServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{ pathParams: RevokeServiceAccountKeyPathParameters },
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof revokeServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{ pathParams: RevokeServiceAccountKeyPathParameters },
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationKey = ['revokeServiceAccountKey'];
|
||||||
|
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 revokeServiceAccountKey>>,
|
||||||
|
{ pathParams: RevokeServiceAccountKeyPathParameters }
|
||||||
|
> = (props) => {
|
||||||
|
const { pathParams } = props ?? {};
|
||||||
|
|
||||||
|
return revokeServiceAccountKey(pathParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RevokeServiceAccountKeyMutationResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof revokeServiceAccountKey>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type RevokeServiceAccountKeyMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Revoke a service account key
|
||||||
|
*/
|
||||||
|
export const useRevokeServiceAccountKey = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof revokeServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{ pathParams: RevokeServiceAccountKeyPathParameters },
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof revokeServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{ pathParams: RevokeServiceAccountKeyPathParameters },
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationOptions = getRevokeServiceAccountKeyMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* This endpoint updates an existing service account key
|
||||||
|
* @summary Updates a service account key
|
||||||
|
*/
|
||||||
|
export const updateServiceAccountKey = (
|
||||||
|
{ id, fid }: UpdateServiceAccountKeyPathParameters,
|
||||||
|
serviceaccounttypesUpdatableFactorAPIKeyDTO: BodyType<ServiceaccounttypesUpdatableFactorAPIKeyDTO>,
|
||||||
|
) => {
|
||||||
|
return GeneratedAPIInstance<string>({
|
||||||
|
url: `/api/v1/service_accounts/${id}/keys/${fid}`,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
data: serviceaccounttypesUpdatableFactorAPIKeyDTO,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUpdateServiceAccountKeyMutationOptions = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableFactorAPIKeyDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableFactorAPIKeyDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationKey = ['updateServiceAccountKey'];
|
||||||
|
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 updateServiceAccountKey>>,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableFactorAPIKeyDTO>;
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
|
const { pathParams, data } = props ?? {};
|
||||||
|
|
||||||
|
return updateServiceAccountKey(pathParams, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateServiceAccountKeyMutationResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountKey>>
|
||||||
|
>;
|
||||||
|
export type UpdateServiceAccountKeyMutationBody = BodyType<ServiceaccounttypesUpdatableFactorAPIKeyDTO>;
|
||||||
|
export type UpdateServiceAccountKeyMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Updates a service account key
|
||||||
|
*/
|
||||||
|
export const useUpdateServiceAccountKey = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableFactorAPIKeyDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountKey>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountKeyPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableFactorAPIKeyDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationOptions = getUpdateServiceAccountKeyMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* This endpoint updates an existing service account status
|
||||||
|
* @summary Updates a service account status
|
||||||
|
*/
|
||||||
|
export const updateServiceAccountStatus = (
|
||||||
|
{ id }: UpdateServiceAccountStatusPathParameters,
|
||||||
|
serviceaccounttypesUpdatableServiceAccountStatusDTO: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>,
|
||||||
|
) => {
|
||||||
|
return GeneratedAPIInstance<string>({
|
||||||
|
url: `/api/v1/service_accounts/${id}/status`,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
data: serviceaccounttypesUpdatableServiceAccountStatusDTO,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUpdateServiceAccountStatusMutationOptions = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountStatus>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountStatusPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountStatus>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountStatusPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationKey = ['updateServiceAccountStatus'];
|
||||||
|
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 updateServiceAccountStatus>>,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountStatusPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
|
const { pathParams, data } = props ?? {};
|
||||||
|
|
||||||
|
return updateServiceAccountStatus(pathParams, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateServiceAccountStatusMutationResult = NonNullable<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountStatus>>
|
||||||
|
>;
|
||||||
|
export type UpdateServiceAccountStatusMutationBody = BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
|
||||||
|
export type UpdateServiceAccountStatusMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Updates a service account status
|
||||||
|
*/
|
||||||
|
export const useUpdateServiceAccountStatus = <
|
||||||
|
TError = ErrorType<RenderErrorResponseDTO>,
|
||||||
|
TContext = unknown
|
||||||
|
>(options?: {
|
||||||
|
mutation?: UseMutationOptions<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountStatus>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountStatusPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
>;
|
||||||
|
}): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof updateServiceAccountStatus>>,
|
||||||
|
TError,
|
||||||
|
{
|
||||||
|
pathParams: UpdateServiceAccountStatusPathParameters;
|
||||||
|
data: BodyType<ServiceaccounttypesUpdatableServiceAccountStatusDTO>;
|
||||||
|
},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
const mutationOptions = getUpdateServiceAccountStatusMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions);
|
||||||
|
};
|
||||||
@@ -2090,6 +2090,154 @@ export interface RoletypesRoleDTO {
|
|||||||
updatedAt?: Date;
|
updatedAt?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ServiceaccounttypesFactorAPIKeyDTO {
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
* @format date-time
|
||||||
|
*/
|
||||||
|
createdAt?: Date;
|
||||||
|
/**
|
||||||
|
* @type integer
|
||||||
|
* @minimum 0
|
||||||
|
*/
|
||||||
|
expires_at: number;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
key: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
* @format date-time
|
||||||
|
*/
|
||||||
|
last_used: Date;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
name?: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
service_account_id: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
* @format date-time
|
||||||
|
*/
|
||||||
|
updatedAt?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceaccounttypesGettableFactorAPIKeyWithKeyDTO {
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceaccounttypesPostableFactorAPIKeyDTO {
|
||||||
|
/**
|
||||||
|
* @type integer
|
||||||
|
* @minimum 0
|
||||||
|
*/
|
||||||
|
expires_at: number;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceaccounttypesPostableServiceAccountDTO {
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
email: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @type array
|
||||||
|
*/
|
||||||
|
roles: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceaccounttypesServiceAccountDTO {
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
* @format date-time
|
||||||
|
*/
|
||||||
|
createdAt?: Date;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
email: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
orgID: string;
|
||||||
|
/**
|
||||||
|
* @type array
|
||||||
|
*/
|
||||||
|
roles: string[];
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
status: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
* @format date-time
|
||||||
|
*/
|
||||||
|
updatedAt?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceaccounttypesUpdatableFactorAPIKeyDTO {
|
||||||
|
/**
|
||||||
|
* @type integer
|
||||||
|
* @minimum 0
|
||||||
|
*/
|
||||||
|
expires_at: number;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceaccounttypesUpdatableServiceAccountDTO {
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
email: string;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @type array
|
||||||
|
*/
|
||||||
|
roles: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceaccounttypesUpdatableServiceAccountStatusDTO {
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
export enum TelemetrytypesFieldContextDTO {
|
export enum TelemetrytypesFieldContextDTO {
|
||||||
metric = 'metric',
|
metric = 'metric',
|
||||||
log = 'log',
|
log = 'log',
|
||||||
@@ -3050,6 +3198,78 @@ export type PatchObjectsPathParameters = {
|
|||||||
id: string;
|
id: string;
|
||||||
relation: string;
|
relation: string;
|
||||||
};
|
};
|
||||||
|
export type ListServiceAccounts200 = {
|
||||||
|
/**
|
||||||
|
* @type array
|
||||||
|
*/
|
||||||
|
data: ServiceaccounttypesServiceAccountDTO[];
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreateServiceAccount201 = {
|
||||||
|
data: TypesIdentifiableDTO;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DeleteServiceAccountPathParameters = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
export type GetServiceAccountPathParameters = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
export type GetServiceAccount200 = {
|
||||||
|
data: ServiceaccounttypesServiceAccountDTO;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateServiceAccountPathParameters = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
export type ListServiceAccountKeysPathParameters = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
export type ListServiceAccountKeys200 = {
|
||||||
|
/**
|
||||||
|
* @type array
|
||||||
|
*/
|
||||||
|
data: ServiceaccounttypesFactorAPIKeyDTO[];
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreateServiceAccountKeyPathParameters = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
export type CreateServiceAccountKey201 = {
|
||||||
|
data: ServiceaccounttypesGettableFactorAPIKeyWithKeyDTO;
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RevokeServiceAccountKeyPathParameters = {
|
||||||
|
id: string;
|
||||||
|
fid: string;
|
||||||
|
};
|
||||||
|
export type UpdateServiceAccountKeyPathParameters = {
|
||||||
|
id: string;
|
||||||
|
fid: string;
|
||||||
|
};
|
||||||
|
export type UpdateServiceAccountStatusPathParameters = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
export type ListUsers200 = {
|
export type ListUsers200 = {
|
||||||
/**
|
/**
|
||||||
* @type array
|
* @type array
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/preference"
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/promote"
|
"github.com/SigNoz/signoz/pkg/modules/promote"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/session"
|
"github.com/SigNoz/signoz/pkg/modules/session"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/user"
|
"github.com/SigNoz/signoz/pkg/modules/user"
|
||||||
"github.com/SigNoz/signoz/pkg/querier"
|
"github.com/SigNoz/signoz/pkg/querier"
|
||||||
@@ -48,6 +49,7 @@ type provider struct {
|
|||||||
authzHandler authz.Handler
|
authzHandler authz.Handler
|
||||||
zeusHandler zeus.Handler
|
zeusHandler zeus.Handler
|
||||||
querierHandler querier.Handler
|
querierHandler querier.Handler
|
||||||
|
serviceAccountHandler serviceaccount.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactory(
|
func NewFactory(
|
||||||
@@ -69,6 +71,7 @@ func NewFactory(
|
|||||||
authzHandler authz.Handler,
|
authzHandler authz.Handler,
|
||||||
zeusHandler zeus.Handler,
|
zeusHandler zeus.Handler,
|
||||||
querierHandler querier.Handler,
|
querierHandler querier.Handler,
|
||||||
|
serviceAccountHandler serviceaccount.Handler,
|
||||||
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
|
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
|
||||||
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
|
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
|
||||||
return newProvider(
|
return newProvider(
|
||||||
@@ -93,6 +96,7 @@ func NewFactory(
|
|||||||
authzHandler,
|
authzHandler,
|
||||||
zeusHandler,
|
zeusHandler,
|
||||||
querierHandler,
|
querierHandler,
|
||||||
|
serviceAccountHandler,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -119,6 +123,7 @@ func newProvider(
|
|||||||
authzHandler authz.Handler,
|
authzHandler authz.Handler,
|
||||||
zeusHandler zeus.Handler,
|
zeusHandler zeus.Handler,
|
||||||
querierHandler querier.Handler,
|
querierHandler querier.Handler,
|
||||||
|
serviceAccountHandler serviceaccount.Handler,
|
||||||
) (apiserver.APIServer, error) {
|
) (apiserver.APIServer, error) {
|
||||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
|
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
|
||||||
router := mux.NewRouter().UseEncodedPath()
|
router := mux.NewRouter().UseEncodedPath()
|
||||||
@@ -143,6 +148,7 @@ func newProvider(
|
|||||||
authzHandler: authzHandler,
|
authzHandler: authzHandler,
|
||||||
zeusHandler: zeusHandler,
|
zeusHandler: zeusHandler,
|
||||||
querierHandler: querierHandler,
|
querierHandler: querierHandler,
|
||||||
|
serviceAccountHandler: serviceAccountHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
|
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
|
||||||
@@ -223,6 +229,10 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := provider.addServiceAccountRoutes(router); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
184
pkg/apiserver/signozapiserver/serviceaccount.go
Normal file
184
pkg/apiserver/signozapiserver/serviceaccount.go
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
package signozapiserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/serviceaccounttypes"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||||
|
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.Create), handler.OpenAPIDef{
|
||||||
|
ID: "CreateServiceAccount",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "Create service account",
|
||||||
|
Description: "This endpoint creates a service account",
|
||||||
|
Request: new(serviceaccounttypes.PostableServiceAccount),
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: new(types.Identifiable),
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusCreated,
|
||||||
|
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.List), handler.OpenAPIDef{
|
||||||
|
ID: "ListServiceAccounts",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "List service accounts",
|
||||||
|
Description: "This endpoint lists the service accounts for an organisation",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: make([]*serviceaccounttypes.ServiceAccount, 0),
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusOK,
|
||||||
|
ErrorStatusCodes: []int{},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/service_accounts/{id}", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.Get), handler.OpenAPIDef{
|
||||||
|
ID: "GetServiceAccount",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "Gets a service account",
|
||||||
|
Description: "This endpoint gets an existing service account",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: new(serviceaccounttypes.ServiceAccount),
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusOK,
|
||||||
|
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/service_accounts/{id}", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.Update), handler.OpenAPIDef{
|
||||||
|
ID: "UpdateServiceAccount",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "Updates a service account",
|
||||||
|
Description: "This endpoint updates an existing service account",
|
||||||
|
Request: new(serviceaccounttypes.UpdatableServiceAccount),
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: nil,
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusNoContent,
|
||||||
|
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusBadRequest},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodPut).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/service_accounts/{id}/status", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.UpdateStatus), handler.OpenAPIDef{
|
||||||
|
ID: "UpdateServiceAccountStatus",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "Updates a service account status",
|
||||||
|
Description: "This endpoint updates an existing service account status",
|
||||||
|
Request: new(serviceaccounttypes.UpdatableServiceAccountStatus),
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: nil,
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusNoContent,
|
||||||
|
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusBadRequest},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodPut).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/service_accounts/{id}", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.Delete), handler.OpenAPIDef{
|
||||||
|
ID: "DeleteServiceAccount",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "Deletes a service account",
|
||||||
|
Description: "This endpoint deletes an existing service account",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: nil,
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusNoContent,
|
||||||
|
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.CreateFactorAPIKey), handler.OpenAPIDef{
|
||||||
|
ID: "CreateServiceAccountKey",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "Create a service account key",
|
||||||
|
Description: "This endpoint creates a service account key",
|
||||||
|
Request: new(serviceaccounttypes.PostableFactorAPIKey),
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: new(serviceaccounttypes.GettableFactorAPIKeyWithKey),
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusCreated,
|
||||||
|
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.ListFactorAPIKey), handler.OpenAPIDef{
|
||||||
|
ID: "ListServiceAccountKeys",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "List service account keys",
|
||||||
|
Description: "This endpoint lists the service account keys",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: make([]*serviceaccounttypes.FactorAPIKey, 0),
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusOK,
|
||||||
|
ErrorStatusCodes: []int{},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.UpdateFactorAPIKey), handler.OpenAPIDef{
|
||||||
|
ID: "UpdateServiceAccountKey",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "Updates a service account key",
|
||||||
|
Description: "This endpoint updates an existing service account key",
|
||||||
|
Request: new(serviceaccounttypes.UpdatableFactorAPIKey),
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: nil,
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusNoContent,
|
||||||
|
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodPut).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authZ.AdminAccess(provider.serviceAccountHandler.RevokeFactorAPIKey), handler.OpenAPIDef{
|
||||||
|
ID: "RevokeServiceAccountKey",
|
||||||
|
Tags: []string{"serviceaccount"},
|
||||||
|
Summary: "Revoke a service account key",
|
||||||
|
Description: "This endpoint revokes an existing service account key",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: nil,
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusNoContent,
|
||||||
|
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -62,14 +62,17 @@ type AuthZ interface {
|
|||||||
// Lists all the roles for the organization filtered by name
|
// Lists all the roles for the organization filtered by name
|
||||||
ListByOrgIDAndNames(context.Context, valuer.UUID, []string) ([]*roletypes.Role, error)
|
ListByOrgIDAndNames(context.Context, valuer.UUID, []string) ([]*roletypes.Role, error)
|
||||||
|
|
||||||
|
// Lists all the roles for the organization filtered by ids
|
||||||
|
ListByOrgIDAndIDs(context.Context, valuer.UUID, []valuer.UUID) ([]*roletypes.Role, error)
|
||||||
|
|
||||||
// Grants a role to the subject based on role name.
|
// Grants a role to the subject based on role name.
|
||||||
Grant(context.Context, valuer.UUID, string, string) error
|
Grant(context.Context, valuer.UUID, []string, string) error
|
||||||
|
|
||||||
// Revokes a granted role from the subject based on role name.
|
// Revokes a granted role from the subject based on role name.
|
||||||
Revoke(context.Context, valuer.UUID, string, string) error
|
Revoke(context.Context, valuer.UUID, []string, string) error
|
||||||
|
|
||||||
// Changes the granted role for the subject based on role name.
|
// Changes the granted role for the subject based on role name.
|
||||||
ModifyGrant(context.Context, valuer.UUID, string, string, string) error
|
ModifyGrant(context.Context, valuer.UUID, []string, []string, string) error
|
||||||
|
|
||||||
// Bootstrap the managed roles.
|
// Bootstrap the managed roles.
|
||||||
CreateManagedRoles(context.Context, valuer.UUID, []*roletypes.Role) error
|
CreateManagedRoles(context.Context, valuer.UUID, []*roletypes.Role) error
|
||||||
|
|||||||
@@ -96,6 +96,39 @@ func (store *store) ListByOrgIDAndNames(ctx context.Context, orgID valuer.UUID,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(roles) != len(names) {
|
||||||
|
return nil, store.sqlstore.WrapNotFoundErrf(
|
||||||
|
nil,
|
||||||
|
roletypes.ErrCodeRoleNotFound,
|
||||||
|
"not all roles found for the provided names: %v", names,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) ListByOrgIDAndIDs(ctx context.Context, orgID valuer.UUID, ids []valuer.UUID) ([]*roletypes.StorableRole, error) {
|
||||||
|
roles := make([]*roletypes.StorableRole, 0)
|
||||||
|
err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewSelect().
|
||||||
|
Model(&roles).
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Where("id IN (?)", bun.In(ids)).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(roles) != len(ids) {
|
||||||
|
return nil, store.sqlstore.WrapNotFoundErrf(
|
||||||
|
nil,
|
||||||
|
roletypes.ErrCodeRoleNotFound,
|
||||||
|
"not all roles found for the provided ids: %v", ids,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return roles, nil
|
return roles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,28 +114,46 @@ func (provider *provider) ListByOrgIDAndNames(ctx context.Context, orgID valuer.
|
|||||||
return roles, nil
|
return roles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Grant(ctx context.Context, orgID valuer.UUID, name string, subject string) error {
|
func (provider *provider) ListByOrgIDAndIDs(ctx context.Context, orgID valuer.UUID, ids []valuer.UUID) ([]*roletypes.Role, error) {
|
||||||
|
storableRoles, err := provider.store.ListByOrgIDAndIDs(ctx, orgID, ids)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
roles := make([]*roletypes.Role, len(storableRoles))
|
||||||
|
for idx, storable := range storableRoles {
|
||||||
|
roles[idx] = roletypes.NewRoleFromStorableRole(storable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Grant(ctx context.Context, orgID valuer.UUID, names []string, subject string) error {
|
||||||
|
selectors := make([]authtypes.Selector, len(names))
|
||||||
|
for idx, name := range names {
|
||||||
|
selectors[idx] = authtypes.MustNewSelector(authtypes.TypeRole, name)
|
||||||
|
}
|
||||||
|
|
||||||
tuples, err := authtypes.TypeableRole.Tuples(
|
tuples, err := authtypes.TypeableRole.Tuples(
|
||||||
subject,
|
subject,
|
||||||
authtypes.RelationAssignee,
|
authtypes.RelationAssignee,
|
||||||
[]authtypes.Selector{
|
selectors,
|
||||||
authtypes.MustNewSelector(authtypes.TypeRole, name),
|
|
||||||
},
|
|
||||||
orgID,
|
orgID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider.Write(ctx, tuples, nil)
|
return provider.Write(ctx, tuples, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) ModifyGrant(ctx context.Context, orgID valuer.UUID, existingRoleName string, updatedRoleName string, subject string) error {
|
func (provider *provider) ModifyGrant(ctx context.Context, orgID valuer.UUID, existingRoleNames []string, updatedRoleNames []string, subject string) error {
|
||||||
err := provider.Revoke(ctx, orgID, existingRoleName, subject)
|
err := provider.Revoke(ctx, orgID, existingRoleNames, subject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = provider.Grant(ctx, orgID, updatedRoleName, subject)
|
err = provider.Grant(ctx, orgID, updatedRoleNames, subject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -143,13 +161,16 @@ func (provider *provider) ModifyGrant(ctx context.Context, orgID valuer.UUID, ex
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Revoke(ctx context.Context, orgID valuer.UUID, name string, subject string) error {
|
func (provider *provider) Revoke(ctx context.Context, orgID valuer.UUID, names []string, subject string) error {
|
||||||
|
selectors := make([]authtypes.Selector, len(names))
|
||||||
|
for idx, name := range names {
|
||||||
|
selectors[idx] = authtypes.MustNewSelector(authtypes.TypeRole, name)
|
||||||
|
}
|
||||||
|
|
||||||
tuples, err := authtypes.TypeableRole.Tuples(
|
tuples, err := authtypes.TypeableRole.Tuples(
|
||||||
subject,
|
subject,
|
||||||
authtypes.RelationAssignee,
|
authtypes.RelationAssignee,
|
||||||
[]authtypes.Selector{
|
selectors,
|
||||||
authtypes.MustNewSelector(authtypes.TypeRole, name),
|
|
||||||
},
|
|
||||||
orgID,
|
orgID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -178,7 +199,7 @@ func (provider *provider) CreateManagedRoles(ctx context.Context, _ valuer.UUID,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) CreateManagedUserRoleTransactions(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) error {
|
func (provider *provider) CreateManagedUserRoleTransactions(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) error {
|
||||||
return provider.Grant(ctx, orgID, roletypes.SigNozAdminRoleName, authtypes.MustNewSubject(authtypes.TypeableUser, userID.String(), orgID, nil))
|
return provider.Grant(ctx, orgID, []string{roletypes.SigNozAdminRoleName}, authtypes.MustNewSubject(authtypes.TypeableUser, userID.String(), orgID, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (setter *provider) Create(_ context.Context, _ valuer.UUID, _ *roletypes.Role) error {
|
func (setter *provider) Create(_ context.Context, _ valuer.UUID, _ *roletypes.Role) error {
|
||||||
|
|||||||
62
pkg/modules/cloudintegration/cloudintegration.go
Normal file
62
pkg/modules/cloudintegration/cloudintegration.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package cloudintegration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Module interface {
|
||||||
|
GetName() cloudintegrationtypes.CloudProviderType
|
||||||
|
|
||||||
|
// AgentCheckIn is called by agent to heartbeat and get latest config in response.
|
||||||
|
AgentCheckIn(ctx context.Context, req *cloudintegrationtypes.PostableAgentCheckInPayload) (any, error)
|
||||||
|
|
||||||
|
GenerateConnectionParams(ctx context.Context) (*cloudintegrationtypes.GettableCloudIntegrationConnectionParams, error)
|
||||||
|
// GenerateConnectionArtifact generates cloud provider specific connection information, client side handles how this information is shown
|
||||||
|
GenerateConnectionArtifact(ctx context.Context, req *cloudintegrationtypes.PostableConnectionArtifact) (any, error)
|
||||||
|
// GetAccountStatus returns agent connection status for a cloud integration account
|
||||||
|
GetAccountStatus(ctx context.Context, orgID, accountID string) (*cloudintegrationtypes.GettableAccountStatus, error)
|
||||||
|
// ListConnectedAccounts lists accounts where agent is connected
|
||||||
|
ListConnectedAccounts(ctx context.Context, orgID string) (*cloudintegrationtypes.GettableConnectedAccountsList, error)
|
||||||
|
|
||||||
|
// LIstServices return list of services for a cloud provider attached with the accountID. This just returns a summary
|
||||||
|
ListServices(ctx context.Context, orgID string, accountID *string) (any, error) // returns either GettableAWSServices or GettableAzureServices
|
||||||
|
// GetServiceDetails returns service definition details for a serviceId. This returns config and other details required to show in service details page on client.
|
||||||
|
GetServiceDetails(ctx context.Context, req *cloudintegrationtypes.GetServiceDetailsReq) (any, error)
|
||||||
|
|
||||||
|
// GetDashboard returns dashboard json for a give cloud integration service dashboard.
|
||||||
|
// this only returns the dashboard when account is connected and service is enabled
|
||||||
|
GetDashboard(ctx context.Context, id string, orgID valuer.UUID) (*dashboardtypes.Dashboard, error)
|
||||||
|
// GetAvailableDashboards returns list of available dashboards across all connected cloud integration accounts in the org.
|
||||||
|
// this list gets added to dashboard list page
|
||||||
|
GetAvailableDashboards(ctx context.Context, orgID valuer.UUID) ([]*dashboardtypes.Dashboard, error)
|
||||||
|
|
||||||
|
// UpdateAccountConfig updates cloud integration account config
|
||||||
|
UpdateAccountConfig(ctx context.Context, orgId valuer.UUID, accountId string, config []byte) (any, error)
|
||||||
|
// UpdateServiceConfig updates cloud integration service config
|
||||||
|
UpdateServiceConfig(ctx context.Context, serviceId string, orgID valuer.UUID, config []byte) (any, error)
|
||||||
|
|
||||||
|
// DisconnectAccount soft deletes/removes a cloud integration account.
|
||||||
|
DisconnectAccount(ctx context.Context, orgID, accountID string) (*cloudintegrationtypes.CloudIntegration, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
AgentCheckIn(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
GenerateConnectionParams(http.ResponseWriter, *http.Request)
|
||||||
|
GenerateConnectionArtifact(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
ListConnectedAccounts(http.ResponseWriter, *http.Request)
|
||||||
|
GetAccountStatus(http.ResponseWriter, *http.Request)
|
||||||
|
ListServices(http.ResponseWriter, *http.Request)
|
||||||
|
GetServiceDetails(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
UpdateAccountConfig(http.ResponseWriter, *http.Request)
|
||||||
|
UpdateServiceConfig(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
DisconnectAccount(http.ResponseWriter, *http.Request)
|
||||||
|
}
|
||||||
193
pkg/modules/cloudintegration/implsqlstore/accounts.go
Normal file
193
pkg/modules/cloudintegration/implsqlstore/accounts.go
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
package implcloudintegration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
cloudintegrationtypes "github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrCodeCloudIntegrationAccountNotFound = errors.MustNewCode("cloud_integration_account_not_found")
|
||||||
|
|
||||||
|
// cloudProviderAccountsSQLRepository is a SQL-backed implementation of CloudIntegrationAccountStore.
|
||||||
|
type cloudProviderAccountsSQLRepository struct {
|
||||||
|
store sqlstore.SQLStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSQLCloudIntegrationAccountStore constructs a SQL-backed CloudIntegrationAccountStore.
|
||||||
|
func NewSQLCloudIntegrationAccountStore(store sqlstore.SQLStore) cloudintegrationtypes.CloudIntegrationAccountStore {
|
||||||
|
return &cloudProviderAccountsSQLRepository{store: store}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------
|
||||||
|
// Account store implementation
|
||||||
|
// -----------------------------
|
||||||
|
|
||||||
|
func (r *cloudProviderAccountsSQLRepository) ListConnected(
|
||||||
|
ctx context.Context, orgId string, cloudProvider string,
|
||||||
|
) ([]cloudintegrationtypes.CloudIntegration, error) {
|
||||||
|
accounts := []cloudintegrationtypes.CloudIntegration{}
|
||||||
|
|
||||||
|
err := r.store.BunDB().NewSelect().
|
||||||
|
Model(&accounts).
|
||||||
|
Where("org_id = ?", orgId).
|
||||||
|
Where("provider = ?", cloudProvider).
|
||||||
|
Where("removed_at is NULL").
|
||||||
|
Where("account_id is not NULL").
|
||||||
|
Where("last_agent_report is not NULL").
|
||||||
|
Order("created_at").
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
slog.ErrorContext(ctx, "error querying connected cloud accounts", "error", err)
|
||||||
|
return nil, errors.WrapInternalf(err, errors.CodeInternal, "could not query connected cloud accounts")
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *cloudProviderAccountsSQLRepository) Get(
|
||||||
|
ctx context.Context, orgId string, provider string, id string,
|
||||||
|
) (*cloudintegrationtypes.CloudIntegration, error) {
|
||||||
|
var result cloudintegrationtypes.CloudIntegration
|
||||||
|
|
||||||
|
err := r.store.BunDB().NewSelect().
|
||||||
|
Model(&result).
|
||||||
|
Where("org_id = ?", orgId).
|
||||||
|
Where("provider = ?", provider).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, errors.WrapNotFoundf(
|
||||||
|
err,
|
||||||
|
ErrCodeCloudIntegrationAccountNotFound,
|
||||||
|
"couldn't find account with Id %s", id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query cloud provider account")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *cloudProviderAccountsSQLRepository) GetConnectedCloudAccount(
|
||||||
|
ctx context.Context, orgId string, provider string, accountId string,
|
||||||
|
) (*cloudintegrationtypes.CloudIntegration, error) {
|
||||||
|
var result cloudintegrationtypes.CloudIntegration
|
||||||
|
|
||||||
|
err := r.store.BunDB().NewSelect().
|
||||||
|
Model(&result).
|
||||||
|
Where("org_id = ?", orgId).
|
||||||
|
Where("provider = ?", provider).
|
||||||
|
Where("account_id = ?", accountId).
|
||||||
|
Where("last_agent_report is not NULL").
|
||||||
|
Where("removed_at is NULL").
|
||||||
|
Scan(ctx)
|
||||||
|
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, errors.WrapNotFoundf(err, ErrCodeCloudIntegrationAccountNotFound, "couldn't find connected cloud account %s", accountId)
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query cloud provider account")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *cloudProviderAccountsSQLRepository) Upsert(
|
||||||
|
ctx context.Context,
|
||||||
|
orgId string,
|
||||||
|
provider string,
|
||||||
|
id *string,
|
||||||
|
config []byte,
|
||||||
|
accountId *string,
|
||||||
|
agentReport *cloudintegrationtypes.AgentReport,
|
||||||
|
removedAt *time.Time,
|
||||||
|
) (*cloudintegrationtypes.CloudIntegration, error) {
|
||||||
|
// Insert
|
||||||
|
if id == nil {
|
||||||
|
temp := valuer.GenerateUUID().StringValue()
|
||||||
|
id = &temp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare clause for setting values in `on conflict do update`
|
||||||
|
onConflictSetStmts := []string{}
|
||||||
|
setColStatement := func(col string) string {
|
||||||
|
return fmt.Sprintf("%s=excluded.%s", col, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config != nil {
|
||||||
|
onConflictSetStmts = append(
|
||||||
|
onConflictSetStmts, setColStatement("config"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if accountId != nil {
|
||||||
|
onConflictSetStmts = append(
|
||||||
|
onConflictSetStmts, setColStatement("account_id"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if agentReport != nil {
|
||||||
|
onConflictSetStmts = append(
|
||||||
|
onConflictSetStmts, setColStatement("last_agent_report"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if removedAt != nil {
|
||||||
|
onConflictSetStmts = append(
|
||||||
|
onConflictSetStmts, setColStatement("removed_at"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set updated_at to current timestamp if it's an upsert
|
||||||
|
onConflictSetStmts = append(
|
||||||
|
onConflictSetStmts, setColStatement("updated_at"),
|
||||||
|
)
|
||||||
|
|
||||||
|
onConflictClause := ""
|
||||||
|
if len(onConflictSetStmts) > 0 {
|
||||||
|
onConflictClause = fmt.Sprintf(
|
||||||
|
"conflict(id, provider, org_id) do update SET\n%s",
|
||||||
|
strings.Join(onConflictSetStmts, ",\n"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
integration := cloudintegrationtypes.CloudIntegration{
|
||||||
|
OrgID: orgId,
|
||||||
|
Provider: provider,
|
||||||
|
Identifiable: types.Identifiable{ID: valuer.MustNewUUID(*id)},
|
||||||
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
Config: string(config),
|
||||||
|
AccountID: accountId,
|
||||||
|
LastAgentReport: agentReport,
|
||||||
|
RemovedAt: removedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := r.store.BunDB().NewInsert().
|
||||||
|
Model(&integration).
|
||||||
|
On(onConflictClause).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't upsert cloud integration account")
|
||||||
|
}
|
||||||
|
|
||||||
|
upsertedAccount, err := r.Get(ctx, orgId, provider, *id)
|
||||||
|
if err != nil {
|
||||||
|
slog.ErrorContext(ctx, "error upserting cloud integration account", "error", err)
|
||||||
|
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't get upserted cloud integration account")
|
||||||
|
}
|
||||||
|
|
||||||
|
return upsertedAccount, nil
|
||||||
|
}
|
||||||
133
pkg/modules/cloudintegration/implsqlstore/serviceconfig.go
Normal file
133
pkg/modules/cloudintegration/implsqlstore/serviceconfig.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package implcloudintegration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
cloudintegrationtypes "github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrCodeServiceConfigNotFound = errors.MustNewCode("service_config_not_found")
|
||||||
|
|
||||||
|
// serviceConfigSQLRepository is a SQL-backed implementation of CloudIntegrationServiceStore.
|
||||||
|
type serviceConfigSQLRepository struct {
|
||||||
|
store sqlstore.SQLStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSQLCloudIntegrationServiceStore constructs a SQL-backed CloudIntegrationServiceStore.
|
||||||
|
func NewSQLCloudIntegrationServiceStore(store sqlstore.SQLStore) cloudintegrationtypes.CloudIntegrationServiceStore {
|
||||||
|
return &serviceConfigSQLRepository{store: store}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------
|
||||||
|
// Service config store implementation
|
||||||
|
// -----------------------------
|
||||||
|
|
||||||
|
func (r *serviceConfigSQLRepository) Get(
|
||||||
|
ctx context.Context,
|
||||||
|
orgID string,
|
||||||
|
cloudAccountId string,
|
||||||
|
serviceType string,
|
||||||
|
) ([]byte, error) {
|
||||||
|
var result cloudintegrationtypes.CloudIntegrationService
|
||||||
|
|
||||||
|
err := r.store.BunDB().NewSelect().
|
||||||
|
Model(&result).
|
||||||
|
Join("JOIN cloud_integration ci ON ci.id = cis.cloud_integration_id").
|
||||||
|
Where("ci.org_id = ?", orgID).
|
||||||
|
Where("ci.id = ?", cloudAccountId).
|
||||||
|
Where("cis.type = ?", serviceType).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, errors.WrapNotFoundf(err, ErrCodeServiceConfigNotFound, "couldn't find config for cloud account %s", cloudAccountId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query cloud service config")
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(result.Config), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *serviceConfigSQLRepository) Upsert(
|
||||||
|
ctx context.Context,
|
||||||
|
orgID string,
|
||||||
|
cloudProvider string,
|
||||||
|
cloudAccountId string,
|
||||||
|
serviceId string,
|
||||||
|
config []byte,
|
||||||
|
) ([]byte, error) {
|
||||||
|
// get cloud integration id from account id
|
||||||
|
// if the account is not connected, we don't need to upsert the config
|
||||||
|
var cloudIntegrationId string
|
||||||
|
err := r.store.BunDB().NewSelect().
|
||||||
|
Model((*cloudintegrationtypes.CloudIntegration)(nil)).
|
||||||
|
Column("id").
|
||||||
|
Where("provider = ?", cloudProvider).
|
||||||
|
Where("account_id = ?", cloudAccountId).
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Where("removed_at is NULL").
|
||||||
|
Where("last_agent_report is not NULL").
|
||||||
|
Scan(ctx, &cloudIntegrationId)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, errors.WrapNotFoundf(
|
||||||
|
err,
|
||||||
|
ErrCodeCloudIntegrationAccountNotFound,
|
||||||
|
"couldn't find active cloud integration account",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query cloud integration id")
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceConfig := cloudintegrationtypes.CloudIntegrationService{
|
||||||
|
Identifiable: types.Identifiable{ID: valuer.GenerateUUID()},
|
||||||
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
Config: string(config),
|
||||||
|
Type: serviceId,
|
||||||
|
CloudIntegrationID: cloudIntegrationId,
|
||||||
|
}
|
||||||
|
_, err = r.store.BunDB().NewInsert().
|
||||||
|
Model(&serviceConfig).
|
||||||
|
On("conflict(cloud_integration_id, type) do update set config=excluded.config, updated_at=excluded.updated_at").
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't upsert cloud service config")
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *serviceConfigSQLRepository) GetAllForAccount(
|
||||||
|
ctx context.Context,
|
||||||
|
orgID string,
|
||||||
|
cloudAccountId string,
|
||||||
|
) (map[string][]byte, error) {
|
||||||
|
var serviceConfigs []cloudintegrationtypes.CloudIntegrationService
|
||||||
|
|
||||||
|
err := r.store.BunDB().NewSelect().
|
||||||
|
Model(&serviceConfigs).
|
||||||
|
Join("JOIN cloud_integration ci ON ci.id = cis.cloud_integration_id").
|
||||||
|
Where("ci.id = ?", cloudAccountId).
|
||||||
|
Where("ci.org_id = ?", orgID).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WrapInternalf(err, errors.CodeInternal, "couldn't query service configs from db")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string][]byte)
|
||||||
|
|
||||||
|
for _, r := range serviceConfigs {
|
||||||
|
result[r.Type] = []byte(r.Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
335
pkg/modules/serviceaccount/implserviceaccount/handler.go
Normal file
335
pkg/modules/serviceaccount/implserviceaccount/handler.go
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
package implserviceaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/http/binding"
|
||||||
|
"github.com/SigNoz/signoz/pkg/http/render"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/serviceaccounttypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
module serviceaccount.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(module serviceaccount.Module) serviceaccount.Handler {
|
||||||
|
return &handler{module: module}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) Create(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(serviceaccounttypes.PostableServiceAccount)
|
||||||
|
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount := serviceaccounttypes.NewServiceAccount(req.Name, req.Email, req.Roles, serviceaccounttypes.StatusActive, valuer.MustNewUUID(claims.OrgID))
|
||||||
|
err = handler.module.Create(ctx, valuer.MustNewUUID(claims.OrgID), serviceAccount)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusCreated, types.Identifiable{ID: serviceAccount.ID})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount, err := handler.module.Get(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusOK, serviceAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) List(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccounts, err := handler.module.List(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusOK, serviceAccounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) Update(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(serviceaccounttypes.UpdatableServiceAccount)
|
||||||
|
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount, err := handler.module.Get(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount.Update(req.Name, req.Email, req.Roles)
|
||||||
|
err = handler.module.Update(ctx, valuer.MustNewUUID(claims.OrgID), serviceAccount)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusNoContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) UpdateStatus(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(serviceaccounttypes.UpdatableServiceAccountStatus)
|
||||||
|
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount, err := handler.module.Get(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount.UpdateStatus(req.Status)
|
||||||
|
err = handler.module.UpdateStatus(ctx, valuer.MustNewUUID(claims.OrgID), serviceAccount)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusNoContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) Delete(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler.module.Delete(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusNoContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) CreateFactorAPIKey(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(serviceaccounttypes.PostableFactorAPIKey)
|
||||||
|
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// this takes care of checking the existence of service account and the org constraint.
|
||||||
|
serviceAccount, err := handler.module.GetWithoutRoles(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
factorAPIKey, err := serviceAccount.NewFactorAPIKey(req.Name, req.ExpiresAt)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler.module.CreateFactorAPIKey(ctx, factorAPIKey)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusCreated, serviceaccounttypes.NewGettableFactorAPIKeyWithKey(factorAPIKey.ID, factorAPIKey.Key))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) ListFactorAPIKey(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount, err := handler.module.GetWithoutRoles(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
factorAPIKeys, err := handler.module.ListFactorAPIKey(ctx, serviceAccount.ID)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusOK, serviceaccounttypes.NewGettableFactorAPIKeys(factorAPIKeys))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) UpdateFactorAPIKey(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
factorAPIKeyID, err := valuer.NewUUID(mux.Vars(r)["fid"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(serviceaccounttypes.UpdatableFactorAPIKey)
|
||||||
|
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount, err := handler.module.GetWithoutRoles(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
factorAPIKey, err := handler.module.GetFactorAPIKey(ctx, serviceAccount.ID, factorAPIKeyID)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
factorAPIKey.Update(req.Name, req.ExpiresAt)
|
||||||
|
err = handler.module.UpdateFactorAPIKey(ctx, serviceAccount.ID, factorAPIKey)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusNoContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *handler) RevokeFactorAPIKey(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
factorAPIKeyID, err := valuer.NewUUID(mux.Vars(r)["fid"])
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount, err := handler.module.GetWithoutRoles(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler.module.RevokeFactorAPIKey(ctx, serviceAccount.ID, factorAPIKeyID)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusNoContent, nil)
|
||||||
|
}
|
||||||
351
pkg/modules/serviceaccount/implserviceaccount/module.go
Normal file
351
pkg/modules/serviceaccount/implserviceaccount/module.go
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
package implserviceaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
|
"github.com/SigNoz/signoz/pkg/emailing"
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/emailtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/serviceaccounttypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type module struct {
|
||||||
|
store serviceaccounttypes.Store
|
||||||
|
authz authz.AuthZ
|
||||||
|
emailing emailing.Emailing
|
||||||
|
settings factory.ScopedProviderSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewModule(store serviceaccounttypes.Store, authz authz.AuthZ, emailing emailing.Emailing, providerSettings factory.ProviderSettings) serviceaccount.Module {
|
||||||
|
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/modules/serviceaccount/implserviceaccount")
|
||||||
|
return &module{store: store, authz: authz, emailing: emailing, settings: settings}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) Create(ctx context.Context, orgID valuer.UUID, serviceAccount *serviceaccounttypes.ServiceAccount) error {
|
||||||
|
// validates the presence of all roles passed in the create request
|
||||||
|
roles, err := module.authz.ListByOrgIDAndNames(ctx, orgID, serviceAccount.Roles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// authz actions cannot run in sql transactions
|
||||||
|
err = module.authz.Grant(ctx, orgID, serviceAccount.Roles, authtypes.MustNewSubject(authtypes.TypeableUser, serviceAccount.ID.String(), orgID, nil))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
storableServiceAccount := serviceaccounttypes.NewStorableServiceAccount(serviceAccount)
|
||||||
|
storableServiceAccountRoles := serviceaccounttypes.NewStorableServiceAccountRoles(serviceAccount.ID, roles)
|
||||||
|
err = module.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||||
|
err := module.store.Create(ctx, storableServiceAccount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = module.store.CreateServiceAccountRoles(ctx, storableServiceAccountRoles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*serviceaccounttypes.ServiceAccount, error) {
|
||||||
|
storableServiceAccount, err := module.store.Get(ctx, orgID, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// did the orchestration on application layer instead of DB as the ORM also does it anyways for many to many tables.
|
||||||
|
storableServiceAccountRoles, err := module.store.GetServiceAccountRoles(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
roleIDs := make([]valuer.UUID, len(storableServiceAccountRoles))
|
||||||
|
for idx, sar := range storableServiceAccountRoles {
|
||||||
|
roleIDs[idx] = valuer.MustNewUUID(sar.RoleID)
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := module.authz.ListByOrgIDAndIDs(ctx, orgID, roleIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rolesNames, err := serviceaccounttypes.NewRolesFromStorableServiceAccountRoles(storableServiceAccountRoles, roles)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount := serviceaccounttypes.NewServiceAccountFromStorables(storableServiceAccount, rolesNames)
|
||||||
|
return serviceAccount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) GetWithoutRoles(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*serviceaccounttypes.ServiceAccount, error) {
|
||||||
|
storableServiceAccount, err := module.store.Get(ctx, orgID, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// passing []string{} (not nil to prevent panics) roles as the function isn't supposed to put roles.
|
||||||
|
serviceAccount := serviceaccounttypes.NewServiceAccountFromStorables(storableServiceAccount, []string{})
|
||||||
|
return serviceAccount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) List(ctx context.Context, orgID valuer.UUID) ([]*serviceaccounttypes.ServiceAccount, error) {
|
||||||
|
storableServiceAccounts, err := module.store.List(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
storableServiceAccountRoles, err := module.store.ListServiceAccountRolesByOrgID(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the service account roles to structured data
|
||||||
|
saIDToRoleIDs, roleIDs := serviceaccounttypes.GetUniqueRolesAndServiceAccountMapping(storableServiceAccountRoles)
|
||||||
|
roles, err := module.authz.ListByOrgIDAndIDs(ctx, orgID, roleIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill in the role fetched data back to service account
|
||||||
|
serviceAccounts := serviceaccounttypes.NewServiceAccountsFromRoles(storableServiceAccounts, roles, saIDToRoleIDs)
|
||||||
|
return serviceAccounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) Update(ctx context.Context, orgID valuer.UUID, input *serviceaccounttypes.ServiceAccount) error {
|
||||||
|
serviceAccount, err := module.Get(ctx, orgID, input.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := module.authz.ListByOrgIDAndNames(ctx, orgID, input.Roles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the role diff if any to modify grants.
|
||||||
|
grants, revokes := serviceAccount.PatchRoles(input)
|
||||||
|
err = module.authz.ModifyGrant(ctx, orgID, revokes, grants, authtypes.MustNewSubject(authtypes.TypeableUser, serviceAccount.ID.String(), orgID, nil))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
storableServiceAccountRoles := serviceaccounttypes.NewStorableServiceAccountRoles(serviceAccount.ID, roles)
|
||||||
|
err = module.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||||
|
err := module.store.Update(ctx, orgID, serviceaccounttypes.NewStorableServiceAccount(input))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete all the service account roles and create new rather than diff here.
|
||||||
|
err = module.store.DeleteServiceAccountRoles(ctx, input.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = module.store.CreateServiceAccountRoles(ctx, storableServiceAccountRoles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) UpdateStatus(ctx context.Context, orgID valuer.UUID, input *serviceaccounttypes.ServiceAccount) error {
|
||||||
|
serviceAccount, err := module.Get(ctx, orgID, input.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.Status == serviceAccount.Status {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch input.Status {
|
||||||
|
case serviceaccounttypes.StatusActive:
|
||||||
|
err := module.activateServiceAccount(ctx, orgID, input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case serviceaccounttypes.StatusDisabled:
|
||||||
|
err := module.disableServiceAccount(ctx, orgID, input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) Delete(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error {
|
||||||
|
serviceAccount, err := module.Get(ctx, orgID, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// revoke from authz first as this cannot run in sql transaction
|
||||||
|
err = module.authz.Revoke(ctx, orgID, serviceAccount.Roles, authtypes.MustNewSubject(authtypes.TypeableUser, serviceAccount.ID.String(), orgID, nil))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = module.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||||
|
err := module.store.DeleteServiceAccountRoles(ctx, serviceAccount.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = module.store.RevokeAllFactorAPIKeys(ctx, serviceAccount.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = module.store.Delete(ctx, serviceAccount.OrgID, serviceAccount.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) CreateFactorAPIKey(ctx context.Context, factorAPIKey *serviceaccounttypes.FactorAPIKey) error {
|
||||||
|
storableFactorAPIKey := serviceaccounttypes.NewStorableFactorAPIKey(factorAPIKey)
|
||||||
|
|
||||||
|
err := module.store.CreateFactorAPIKey(ctx, storableFactorAPIKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount, err := module.store.GetByID(ctx, factorAPIKey.ServiceAccountID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := module.emailing.SendHTML(ctx, serviceAccount.Email, "New API Key created for your SigNoz account", emailtypes.TemplateNameAPIKeyEvent, map[string]any{
|
||||||
|
"Name": serviceAccount.Name,
|
||||||
|
"KeyName": factorAPIKey.Name,
|
||||||
|
"KeyID": factorAPIKey.ID.String(),
|
||||||
|
"KeyCreatedAt": factorAPIKey.CreatedAt.String(),
|
||||||
|
}); err != nil {
|
||||||
|
module.settings.Logger().ErrorContext(ctx, "failed to send email", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) GetFactorAPIKey(ctx context.Context, serviceAccountID valuer.UUID, id valuer.UUID) (*serviceaccounttypes.FactorAPIKey, error) {
|
||||||
|
storableFactorAPIKey, err := module.store.GetFactorAPIKey(ctx, serviceAccountID, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceaccounttypes.NewFactorAPIKeyFromStorable(storableFactorAPIKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) ListFactorAPIKey(ctx context.Context, serviceAccountID valuer.UUID) ([]*serviceaccounttypes.FactorAPIKey, error) {
|
||||||
|
storables, err := module.store.ListFactorAPIKey(ctx, serviceAccountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceaccounttypes.NewFactorAPIKeyFromStorables(storables), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) UpdateFactorAPIKey(ctx context.Context, serviceAccountID valuer.UUID, factorAPIKey *serviceaccounttypes.FactorAPIKey) error {
|
||||||
|
return module.store.UpdateFactorAPIKey(ctx, serviceAccountID, serviceaccounttypes.NewStorableFactorAPIKey(factorAPIKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) RevokeFactorAPIKey(ctx context.Context, serviceAccountID valuer.UUID, id valuer.UUID) error {
|
||||||
|
factorAPIKey, err := module.GetFactorAPIKey(ctx, serviceAccountID, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = module.store.RevokeFactorAPIKey(ctx, serviceAccountID, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceAccount, err := module.store.GetByID(ctx, serviceAccountID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := module.emailing.SendHTML(ctx, serviceAccount.Email, "API Key revoked for your SigNoz account", emailtypes.TemplateNameAPIKeyEvent, map[string]any{
|
||||||
|
"Name": serviceAccount.Name,
|
||||||
|
"KeyName": factorAPIKey.Name,
|
||||||
|
"KeyID": factorAPIKey.ID.String(),
|
||||||
|
"KeyCreatedAt": factorAPIKey.CreatedAt.String(),
|
||||||
|
}); err != nil {
|
||||||
|
module.settings.Logger().ErrorContext(ctx, "failed to send email", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) disableServiceAccount(ctx context.Context, orgID valuer.UUID, input *serviceaccounttypes.ServiceAccount) error {
|
||||||
|
err := module.authz.Revoke(ctx, orgID, input.Roles, authtypes.MustNewSubject(authtypes.TypeableUser, input.ID.String(), orgID, nil))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = module.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||||
|
// revoke all the API keys on disable
|
||||||
|
err := module.store.RevokeAllFactorAPIKeys(ctx, input.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the status but do not delete the role mappings as we will reuse them on activation.
|
||||||
|
err = module.Update(ctx, orgID, input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *module) activateServiceAccount(ctx context.Context, orgID valuer.UUID, input *serviceaccounttypes.ServiceAccount) error {
|
||||||
|
err := module.authz.Grant(ctx, orgID, input.Roles, authtypes.MustNewSubject(authtypes.TypeableUser, input.ID.String(), orgID, nil))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = module.Update(ctx, orgID, input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
282
pkg/modules/serviceaccount/implserviceaccount/store.go
Normal file
282
pkg/modules/serviceaccount/implserviceaccount/store.go
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
package implserviceaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/serviceaccounttypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type store struct {
|
||||||
|
sqlstore sqlstore.SQLStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStore(sqlstore sqlstore.SQLStore) serviceaccounttypes.Store {
|
||||||
|
return &store{sqlstore: sqlstore}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) Create(ctx context.Context, storable *serviceaccounttypes.StorableServiceAccount) error {
|
||||||
|
_, err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewInsert().
|
||||||
|
Model(storable).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return store.sqlstore.WrapAlreadyExistsErrf(err, serviceaccounttypes.ErrCodeServiceAccountAlreadyExists, "service account with id: %s already exists", storable.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*serviceaccounttypes.StorableServiceAccount, error) {
|
||||||
|
storable := new(serviceaccounttypes.StorableServiceAccount)
|
||||||
|
|
||||||
|
err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewSelect().
|
||||||
|
Model(storable).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, store.sqlstore.WrapNotFoundErrf(err, serviceaccounttypes.ErrCodeServiceAccountNotFound, "service account with id: %s doesn't exist in org: %s", id, orgID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return storable, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) GetByID(ctx context.Context, id valuer.UUID) (*serviceaccounttypes.StorableServiceAccount, error) {
|
||||||
|
storable := new(serviceaccounttypes.StorableServiceAccount)
|
||||||
|
|
||||||
|
err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewSelect().
|
||||||
|
Model(storable).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, store.sqlstore.WrapNotFoundErrf(err, serviceaccounttypes.ErrCodeServiceAccountNotFound, "service account with id: %s doesn't exist", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return storable, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) List(ctx context.Context, orgID valuer.UUID) ([]*serviceaccounttypes.StorableServiceAccount, error) {
|
||||||
|
storables := make([]*serviceaccounttypes.StorableServiceAccount, 0)
|
||||||
|
|
||||||
|
err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewSelect().
|
||||||
|
Model(&storables).
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return storables, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) Update(ctx context.Context, orgID valuer.UUID, storable *serviceaccounttypes.StorableServiceAccount) error {
|
||||||
|
_, err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewUpdate().
|
||||||
|
Model(storable).
|
||||||
|
WherePK().
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) Delete(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error {
|
||||||
|
_, err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewDelete().
|
||||||
|
Model(new(serviceaccounttypes.StorableServiceAccount)).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) CreateServiceAccountRoles(ctx context.Context, storables []*serviceaccounttypes.StorableServiceAccountRole) error {
|
||||||
|
_, err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewInsert().
|
||||||
|
Model(&storables).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return store.sqlstore.WrapAlreadyExistsErrf(err, serviceaccounttypes.ErrCodeServiceAccountRoleAlreadyExists, "duplicate role assignments for service account")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) GetServiceAccountRoles(ctx context.Context, id valuer.UUID) ([]*serviceaccounttypes.StorableServiceAccountRole, error) {
|
||||||
|
storables := make([]*serviceaccounttypes.StorableServiceAccountRole, 0)
|
||||||
|
|
||||||
|
err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewSelect().
|
||||||
|
Model(&storables).
|
||||||
|
Where("service_account_id = ?", id).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
// no need to wrap not found here as this is many to many table
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return storables, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) ListServiceAccountRolesByOrgID(ctx context.Context, orgID valuer.UUID) ([]*serviceaccounttypes.StorableServiceAccountRole, error) {
|
||||||
|
storables := make([]*serviceaccounttypes.StorableServiceAccountRole, 0)
|
||||||
|
|
||||||
|
err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewSelect().
|
||||||
|
Model(&storables).
|
||||||
|
Join("JOIN service_account").
|
||||||
|
JoinOn("service_account.id = service_account_role.service_account_id").
|
||||||
|
Where("service_account.org_id = ?", orgID).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return storables, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) DeleteServiceAccountRoles(ctx context.Context, id valuer.UUID) error {
|
||||||
|
_, err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewDelete().
|
||||||
|
Model(new(serviceaccounttypes.StorableServiceAccountRole)).
|
||||||
|
Where("service_account_id = ?", id).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) CreateFactorAPIKey(ctx context.Context, storable *serviceaccounttypes.StorableFactorAPIKey) error {
|
||||||
|
_, err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewInsert().
|
||||||
|
Model(storable).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return store.sqlstore.WrapAlreadyExistsErrf(err, serviceaccounttypes.ErrCodeServiceAccountFactorAPIKeyAlreadyExists, "api key with name: %s already exists for service account: %s", storable.Name, storable.ServiceAccountID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) GetFactorAPIKey(ctx context.Context, serviceAccountID valuer.UUID, id valuer.UUID) (*serviceaccounttypes.StorableFactorAPIKey, error) {
|
||||||
|
storable := new(serviceaccounttypes.StorableFactorAPIKey)
|
||||||
|
|
||||||
|
err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewSelect().
|
||||||
|
Model(storable).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Where("service_account_id = ?", serviceAccountID).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, store.sqlstore.WrapNotFoundErrf(err, serviceaccounttypes.ErrCodeServiceAccounFactorAPIKeytNotFound, "api key with id: %s doesn't exist for service account: %s", id, serviceAccountID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return storable, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) ListFactorAPIKey(ctx context.Context, serviceAccountID valuer.UUID) ([]*serviceaccounttypes.StorableFactorAPIKey, error) {
|
||||||
|
storables := make([]*serviceaccounttypes.StorableFactorAPIKey, 0)
|
||||||
|
|
||||||
|
err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewSelect().
|
||||||
|
Model(&storables).
|
||||||
|
Where("service_account_id = ?", serviceAccountID).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return storables, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) UpdateFactorAPIKey(ctx context.Context, serviceAccountID valuer.UUID, storable *serviceaccounttypes.StorableFactorAPIKey) error {
|
||||||
|
_, err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewUpdate().
|
||||||
|
Model(storable).
|
||||||
|
Where("service_account_id = ?", serviceAccountID).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) RevokeFactorAPIKey(ctx context.Context, serviceAccountID valuer.UUID, id valuer.UUID) error {
|
||||||
|
_, err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewDelete().
|
||||||
|
Model(new(serviceaccounttypes.StorableFactorAPIKey)).
|
||||||
|
Where("service_account_id = ?", serviceAccountID).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) RevokeAllFactorAPIKeys(ctx context.Context, serviceAccountID valuer.UUID) error {
|
||||||
|
_, err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewDelete().
|
||||||
|
Model(new(serviceaccounttypes.StorableFactorAPIKey)).
|
||||||
|
Where("service_account_id = ?", serviceAccountID).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) RunInTx(ctx context.Context, cb func(context.Context) error) error {
|
||||||
|
return store.sqlstore.RunInTxCtx(ctx, nil, func(ctx context.Context) error {
|
||||||
|
return cb(ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
69
pkg/modules/serviceaccount/serviceaccount.go
Normal file
69
pkg/modules/serviceaccount/serviceaccount.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package serviceaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/serviceaccounttypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Module interface {
|
||||||
|
// Creates a new service account for an organization.
|
||||||
|
Create(context.Context, valuer.UUID, *serviceaccounttypes.ServiceAccount) error
|
||||||
|
|
||||||
|
// Gets a service account by id.
|
||||||
|
Get(context.Context, valuer.UUID, valuer.UUID) (*serviceaccounttypes.ServiceAccount, error)
|
||||||
|
|
||||||
|
// Gets a service account by id without fetching roles.
|
||||||
|
GetWithoutRoles(context.Context, valuer.UUID, valuer.UUID) (*serviceaccounttypes.ServiceAccount, error)
|
||||||
|
|
||||||
|
// List all service accounts for an organization.
|
||||||
|
List(context.Context, valuer.UUID) ([]*serviceaccounttypes.ServiceAccount, error)
|
||||||
|
|
||||||
|
// Updates an existing service account
|
||||||
|
Update(context.Context, valuer.UUID, *serviceaccounttypes.ServiceAccount) error
|
||||||
|
|
||||||
|
// Updates an existing service account status
|
||||||
|
UpdateStatus(context.Context, valuer.UUID, *serviceaccounttypes.ServiceAccount) error
|
||||||
|
|
||||||
|
// Deletes an existing service account by id
|
||||||
|
Delete(context.Context, valuer.UUID, valuer.UUID) error
|
||||||
|
|
||||||
|
// Creates a new API key for a service account
|
||||||
|
CreateFactorAPIKey(context.Context, *serviceaccounttypes.FactorAPIKey) error
|
||||||
|
|
||||||
|
// Gets a factor API key by id
|
||||||
|
GetFactorAPIKey(context.Context, valuer.UUID, valuer.UUID) (*serviceaccounttypes.FactorAPIKey, error)
|
||||||
|
|
||||||
|
// Lists all the API keys for a service account
|
||||||
|
ListFactorAPIKey(context.Context, valuer.UUID) ([]*serviceaccounttypes.FactorAPIKey, error)
|
||||||
|
|
||||||
|
// Updates an existing API key for a service account
|
||||||
|
UpdateFactorAPIKey(context.Context, valuer.UUID, *serviceaccounttypes.FactorAPIKey) error
|
||||||
|
|
||||||
|
// Revokes an existing API key for a service account
|
||||||
|
RevokeFactorAPIKey(context.Context, valuer.UUID, valuer.UUID) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
Create(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
Get(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
List(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
Update(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
UpdateStatus(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
Delete(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
CreateFactorAPIKey(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
ListFactorAPIKey(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
UpdateFactorAPIKey(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
RevokeFactorAPIKey(http.ResponseWriter, *http.Request)
|
||||||
|
}
|
||||||
@@ -175,7 +175,7 @@ func (module *Module) CreateUser(ctx context.Context, input *types.User, opts ..
|
|||||||
createUserOpts := root.NewCreateUserOptions(opts...)
|
createUserOpts := root.NewCreateUserOptions(opts...)
|
||||||
|
|
||||||
// since assign is idempotant multiple calls to assign won't cause issues in case of retries.
|
// since assign is idempotant multiple calls to assign won't cause issues in case of retries.
|
||||||
err := module.authz.Grant(ctx, input.OrgID, roletypes.MustGetSigNozManagedRoleFromExistingRole(input.Role), authtypes.MustNewSubject(authtypes.TypeableUser, input.ID.StringValue(), input.OrgID, nil))
|
err := module.authz.Grant(ctx, input.OrgID, []string{roletypes.MustGetSigNozManagedRoleFromExistingRole(input.Role)}, authtypes.MustNewSubject(authtypes.TypeableUser, input.ID.StringValue(), input.OrgID, nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -237,8 +237,8 @@ func (m *Module) UpdateUser(ctx context.Context, orgID valuer.UUID, id string, u
|
|||||||
if user.Role != "" && user.Role != existingUser.Role {
|
if user.Role != "" && user.Role != existingUser.Role {
|
||||||
err = m.authz.ModifyGrant(ctx,
|
err = m.authz.ModifyGrant(ctx,
|
||||||
orgID,
|
orgID,
|
||||||
roletypes.MustGetSigNozManagedRoleFromExistingRole(existingUser.Role),
|
[]string{roletypes.MustGetSigNozManagedRoleFromExistingRole(existingUser.Role)},
|
||||||
roletypes.MustGetSigNozManagedRoleFromExistingRole(user.Role),
|
[]string{roletypes.MustGetSigNozManagedRoleFromExistingRole(user.Role)},
|
||||||
authtypes.MustNewSubject(authtypes.TypeableUser, id, orgID, nil),
|
authtypes.MustNewSubject(authtypes.TypeableUser, id, orgID, nil),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -295,7 +295,7 @@ func (module *Module) DeleteUser(ctx context.Context, orgID valuer.UUID, id stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// since revoke is idempotant multiple calls to revoke won't cause issues in case of retries
|
// since revoke is idempotant multiple calls to revoke won't cause issues in case of retries
|
||||||
err = module.authz.Revoke(ctx, orgID, roletypes.MustGetSigNozManagedRoleFromExistingRole(user.Role), authtypes.MustNewSubject(authtypes.TypeableUser, id, orgID, nil))
|
err = module.authz.Revoke(ctx, orgID, []string{roletypes.MustGetSigNozManagedRoleFromExistingRole(user.Role)}, authtypes.MustNewSubject(authtypes.TypeableUser, id, orgID, nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,8 +159,8 @@ func (s *service) createOrPromoteRootUser(ctx context.Context, orgID valuer.UUID
|
|||||||
if oldRole != types.RoleAdmin {
|
if oldRole != types.RoleAdmin {
|
||||||
if err := s.authz.ModifyGrant(ctx,
|
if err := s.authz.ModifyGrant(ctx,
|
||||||
orgID,
|
orgID,
|
||||||
roletypes.MustGetSigNozManagedRoleFromExistingRole(oldRole),
|
[]string{roletypes.MustGetSigNozManagedRoleFromExistingRole(oldRole)},
|
||||||
roletypes.MustGetSigNozManagedRoleFromExistingRole(types.RoleAdmin),
|
[]string{roletypes.MustGetSigNozManagedRoleFromExistingRole(types.RoleAdmin)},
|
||||||
authtypes.MustNewSubject(authtypes.TypeableUser, existingUser.ID.StringValue(), orgID, nil),
|
authtypes.MustNewSubject(authtypes.TypeableUser, existingUser.ID.StringValue(), orgID, nil),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/serviceaccount/implserviceaccount"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/services"
|
"github.com/SigNoz/signoz/pkg/modules/services"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/services/implservices"
|
"github.com/SigNoz/signoz/pkg/modules/services/implservices"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/spanpercentile"
|
"github.com/SigNoz/signoz/pkg/modules/spanpercentile"
|
||||||
@@ -36,22 +38,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Handlers struct {
|
type Handlers struct {
|
||||||
SavedView savedview.Handler
|
SavedView savedview.Handler
|
||||||
Apdex apdex.Handler
|
Apdex apdex.Handler
|
||||||
Dashboard dashboard.Handler
|
Dashboard dashboard.Handler
|
||||||
QuickFilter quickfilter.Handler
|
QuickFilter quickfilter.Handler
|
||||||
TraceFunnel tracefunnel.Handler
|
TraceFunnel tracefunnel.Handler
|
||||||
RawDataExport rawdataexport.Handler
|
RawDataExport rawdataexport.Handler
|
||||||
SpanPercentile spanpercentile.Handler
|
SpanPercentile spanpercentile.Handler
|
||||||
Services services.Handler
|
Services services.Handler
|
||||||
MetricsExplorer metricsexplorer.Handler
|
MetricsExplorer metricsexplorer.Handler
|
||||||
Global global.Handler
|
Global global.Handler
|
||||||
FlaggerHandler flagger.Handler
|
FlaggerHandler flagger.Handler
|
||||||
GatewayHandler gateway.Handler
|
GatewayHandler gateway.Handler
|
||||||
Fields fields.Handler
|
Fields fields.Handler
|
||||||
AuthzHandler authz.Handler
|
AuthzHandler authz.Handler
|
||||||
ZeusHandler zeus.Handler
|
ZeusHandler zeus.Handler
|
||||||
QuerierHandler querier.Handler
|
QuerierHandler querier.Handler
|
||||||
|
ServiceAccountHandler serviceaccount.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandlers(
|
func NewHandlers(
|
||||||
@@ -68,21 +71,22 @@ func NewHandlers(
|
|||||||
zeusService zeus.Zeus,
|
zeusService zeus.Zeus,
|
||||||
) Handlers {
|
) Handlers {
|
||||||
return Handlers{
|
return Handlers{
|
||||||
SavedView: implsavedview.NewHandler(modules.SavedView),
|
SavedView: implsavedview.NewHandler(modules.SavedView),
|
||||||
Apdex: implapdex.NewHandler(modules.Apdex),
|
Apdex: implapdex.NewHandler(modules.Apdex),
|
||||||
Dashboard: impldashboard.NewHandler(modules.Dashboard, providerSettings),
|
Dashboard: impldashboard.NewHandler(modules.Dashboard, providerSettings),
|
||||||
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
|
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
|
||||||
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
|
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
|
||||||
RawDataExport: implrawdataexport.NewHandler(modules.RawDataExport),
|
RawDataExport: implrawdataexport.NewHandler(modules.RawDataExport),
|
||||||
Services: implservices.NewHandler(modules.Services),
|
Services: implservices.NewHandler(modules.Services),
|
||||||
MetricsExplorer: implmetricsexplorer.NewHandler(modules.MetricsExplorer),
|
MetricsExplorer: implmetricsexplorer.NewHandler(modules.MetricsExplorer),
|
||||||
SpanPercentile: implspanpercentile.NewHandler(modules.SpanPercentile),
|
SpanPercentile: implspanpercentile.NewHandler(modules.SpanPercentile),
|
||||||
Global: signozglobal.NewHandler(global),
|
Global: signozglobal.NewHandler(global),
|
||||||
FlaggerHandler: flagger.NewHandler(flaggerService),
|
FlaggerHandler: flagger.NewHandler(flaggerService),
|
||||||
GatewayHandler: gateway.NewHandler(gatewayService),
|
GatewayHandler: gateway.NewHandler(gatewayService),
|
||||||
Fields: implfields.NewHandler(providerSettings, telemetryMetadataStore),
|
Fields: implfields.NewHandler(providerSettings, telemetryMetadataStore),
|
||||||
AuthzHandler: signozauthzapi.NewHandler(authz),
|
AuthzHandler: signozauthzapi.NewHandler(authz),
|
||||||
ZeusHandler: zeus.NewHandler(zeusService, licensing),
|
ZeusHandler: zeus.NewHandler(zeusService, licensing),
|
||||||
QuerierHandler: querierHandler,
|
QuerierHandler: querierHandler,
|
||||||
|
ServiceAccountHandler: implserviceaccount.NewHandler(modules.ServiceAccount),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/serviceaccount/implserviceaccount"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/services"
|
"github.com/SigNoz/signoz/pkg/modules/services"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/services/implservices"
|
"github.com/SigNoz/signoz/pkg/modules/services/implservices"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/session"
|
"github.com/SigNoz/signoz/pkg/modules/session"
|
||||||
@@ -66,6 +68,7 @@ type Modules struct {
|
|||||||
SpanPercentile spanpercentile.Module
|
SpanPercentile spanpercentile.Module
|
||||||
MetricsExplorer metricsexplorer.Module
|
MetricsExplorer metricsexplorer.Module
|
||||||
Promote promote.Module
|
Promote promote.Module
|
||||||
|
ServiceAccount serviceaccount.Module
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModules(
|
func NewModules(
|
||||||
@@ -110,5 +113,6 @@ func NewModules(
|
|||||||
Services: implservices.NewModule(querier, telemetryStore),
|
Services: implservices.NewModule(querier, telemetryStore),
|
||||||
MetricsExplorer: implmetricsexplorer.NewModule(telemetryStore, telemetryMetadataStore, cache, ruleStore, dashboard, providerSettings, config.MetricsExplorer),
|
MetricsExplorer: implmetricsexplorer.NewModule(telemetryStore, telemetryMetadataStore, cache, ruleStore, dashboard, providerSettings, config.MetricsExplorer),
|
||||||
Promote: implpromote.NewModule(telemetryMetadataStore, telemetryStore),
|
Promote: implpromote.NewModule(telemetryMetadataStore, telemetryStore),
|
||||||
|
ServiceAccount: implserviceaccount.NewModule(implserviceaccount.NewStore(sqlstore), authz, emailing, providerSettings),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/preference"
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/promote"
|
"github.com/SigNoz/signoz/pkg/modules/promote"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/session"
|
"github.com/SigNoz/signoz/pkg/modules/session"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/user"
|
"github.com/SigNoz/signoz/pkg/modules/user"
|
||||||
"github.com/SigNoz/signoz/pkg/querier"
|
"github.com/SigNoz/signoz/pkg/querier"
|
||||||
@@ -59,6 +60,7 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta
|
|||||||
struct{ authz.Handler }{},
|
struct{ authz.Handler }{},
|
||||||
struct{ zeus.Handler }{},
|
struct{ zeus.Handler }{},
|
||||||
struct{ querier.Handler }{},
|
struct{ querier.Handler }{},
|
||||||
|
struct{ serviceaccount.Handler }{},
|
||||||
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
|
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -255,6 +255,7 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
|
|||||||
handlers.AuthzHandler,
|
handlers.AuthzHandler,
|
||||||
handlers.ZeusHandler,
|
handlers.ZeusHandler,
|
||||||
handlers.QuerierHandler,
|
handlers.QuerierHandler,
|
||||||
|
handlers.ServiceAccountHandler,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/errors"
|
|
||||||
"github.com/SigNoz/signoz/pkg/factory"
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
"github.com/SigNoz/signoz/pkg/flagger"
|
"github.com/SigNoz/signoz/pkg/flagger"
|
||||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||||
@@ -189,9 +188,7 @@ func (b *MetricQueryStatementBuilder) buildPipelineStatement(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// spatial_aggregation_cte
|
// spatial_aggregation_cte
|
||||||
if frag, args, err := b.buildSpatialAggregationCTE(ctx, start, end, query, keys); err != nil {
|
if frag, args := b.buildSpatialAggregationCTE(ctx, start, end, query, keys); frag != "" {
|
||||||
return nil, err
|
|
||||||
} else if frag != "" {
|
|
||||||
cteFragments = append(cteFragments, frag)
|
cteFragments = append(cteFragments, frag)
|
||||||
cteArgs = append(cteArgs, args)
|
cteArgs = append(cteArgs, args)
|
||||||
}
|
}
|
||||||
@@ -522,14 +519,7 @@ func (b *MetricQueryStatementBuilder) buildSpatialAggregationCTE(
|
|||||||
_ uint64,
|
_ uint64,
|
||||||
query qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation],
|
query qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation],
|
||||||
_ map[string][]*telemetrytypes.TelemetryFieldKey,
|
_ map[string][]*telemetrytypes.TelemetryFieldKey,
|
||||||
) (string, []any, error) {
|
) (string, []any) {
|
||||||
if query.Aggregations[0].SpaceAggregation.IsZero() {
|
|
||||||
return "", nil, errors.Newf(
|
|
||||||
errors.TypeInvalidInput,
|
|
||||||
errors.CodeInvalidInput,
|
|
||||||
"invalid space aggregation, should be one of the following: [`sum`, `avg`, `min`, `max`, `count`, `p50`, `p75`, `p90`, `p95`, `p99`]",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sb := sqlbuilder.NewSelectBuilder()
|
sb := sqlbuilder.NewSelectBuilder()
|
||||||
|
|
||||||
sb.Select("ts")
|
sb.Select("ts")
|
||||||
@@ -546,7 +536,7 @@ func (b *MetricQueryStatementBuilder) buildSpatialAggregationCTE(
|
|||||||
sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...)
|
sb.GroupBy(querybuilder.GroupByKeys(query.GroupBy)...)
|
||||||
|
|
||||||
q, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
q, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||||
return fmt.Sprintf("__spatial_aggregation_cte AS (%s)", q), args, nil
|
return fmt.Sprintf("__spatial_aggregation_cte AS (%s)", q), args
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *MetricQueryStatementBuilder) BuildFinalSelect(
|
func (b *MetricQueryStatementBuilder) BuildFinalSelect(
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ func TestStatementBuilder(t *testing.T) {
|
|||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test_histogram_percentile",
|
name: "test_histogram_percentile1",
|
||||||
requestType: qbtypes.RequestTypeTimeSeries,
|
requestType: qbtypes.RequestTypeTimeSeries,
|
||||||
query: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
query: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||||
Signal: telemetrytypes.SignalMetrics,
|
Signal: telemetrytypes.SignalMetrics,
|
||||||
@@ -132,6 +132,7 @@ func TestStatementBuilder(t *testing.T) {
|
|||||||
MetricName: "signoz_latency",
|
MetricName: "signoz_latency",
|
||||||
Type: metrictypes.HistogramType,
|
Type: metrictypes.HistogramType,
|
||||||
Temporality: metrictypes.Delta,
|
Temporality: metrictypes.Delta,
|
||||||
|
TimeAggregation: metrictypes.TimeAggregationRate,
|
||||||
SpaceAggregation: metrictypes.SpaceAggregationPercentile95,
|
SpaceAggregation: metrictypes.SpaceAggregationPercentile95,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -187,7 +188,7 @@ func TestStatementBuilder(t *testing.T) {
|
|||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test_histogram_percentile",
|
name: "test_histogram_percentile2",
|
||||||
requestType: qbtypes.RequestTypeTimeSeries,
|
requestType: qbtypes.RequestTypeTimeSeries,
|
||||||
query: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
query: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||||
Signal: telemetrytypes.SignalMetrics,
|
Signal: telemetrytypes.SignalMetrics,
|
||||||
@@ -197,6 +198,7 @@ func TestStatementBuilder(t *testing.T) {
|
|||||||
MetricName: "http_server_duration_bucket",
|
MetricName: "http_server_duration_bucket",
|
||||||
Type: metrictypes.HistogramType,
|
Type: metrictypes.HistogramType,
|
||||||
Temporality: metrictypes.Cumulative,
|
Temporality: metrictypes.Cumulative,
|
||||||
|
TimeAggregation: metrictypes.TimeAggregationRate,
|
||||||
SpaceAggregation: metrictypes.SpaceAggregationPercentile95,
|
SpaceAggregation: metrictypes.SpaceAggregationPercentile95,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -211,7 +213,7 @@ func TestStatementBuilder(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: qbtypes.Statement{
|
expected: qbtypes.Statement{
|
||||||
Query: "WITH __temporal_aggregation_cte AS (SELECT ts, `service.name`, `le`, multiIf(row_number() OVER rate_window = 1, nan, (per_series_value - lagInFrame(per_series_value, 1) OVER rate_window) < 0, per_series_value / (ts - lagInFrame(ts, 1) OVER rate_window), (per_series_value - lagInFrame(per_series_value, 1) OVER rate_window) / (ts - lagInFrame(ts, 1) OVER rate_window)) AS per_series_value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), toIntervalSecond(30)) AS ts, `service.name`, `le`, max(value) AS per_series_value FROM signoz_metrics.distributed_samples_v4 AS points INNER JOIN (SELECT fingerprint, JSONExtractString(labels, 'service.name') AS `service.name`, JSONExtractString(labels, 'le') AS `le` FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name IN (?) AND unix_milli >= ? AND unix_milli <= ? AND LOWER(temporality) LIKE LOWER(?) AND __normalized = ? GROUP BY fingerprint, `service.name`, `le`) AS filtered_time_series ON points.fingerprint = filtered_time_series.fingerprint WHERE metric_name IN (?) AND unix_milli >= ? AND unix_milli < ? GROUP BY fingerprint, ts, `service.name`, `le` ORDER BY fingerprint, ts) WINDOW rate_window AS (PARTITION BY fingerprint ORDER BY fingerprint, ts)), __spatial_aggregation_cte AS (SELECT ts, `service.name`, `le`, sum(per_series_value) AS value FROM __temporal_aggregation_cte WHERE isNaN(per_series_value) = ? GROUP BY ts, `service.name`, `le`) SELECT ts, `service.name`, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.950) AS value FROM __spatial_aggregation_cte GROUP BY `service.name`, ts ORDER BY `service.name`, ts",
|
Query: "WITH __temporal_aggregation_cte AS (SELECT ts, `service.name`, `le`, multiIf(row_number() OVER rate_window = 1, nan, (per_series_value - lagInFrame(per_series_value, 1) OVER rate_window) < 0, per_series_value / (ts - lagInFrame(ts, 1) OVER rate_window), (per_series_value - lagInFrame(per_series_value, 1) OVER rate_window) / (ts - lagInFrame(ts, 1) OVER rate_window)) AS per_series_value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), toIntervalSecond(30)) AS ts, `service.name`, `le`, max(value) AS per_series_value FROM signoz_metrics.distributed_samples_v4 AS points INNER JOIN (SELECT fingerprint, JSONExtractString(labels, 'service.name') AS `service.name`, JSONExtractString(labels, 'le') AS `le` FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name IN (?) AND unix_milli >= ? AND unix_milli <= ? AND LOWER(temporality) LIKE LOWER(?) AND __normalized = ? GROUP BY fingerprint, `service.name`, `le`) AS filtered_time_series ON points.fingerprint = filtered_time_series.fingerprint WHERE metric_name IN (?) AND unix_milli >= ? AND unix_milli < ? GROUP BY fingerprint, ts, `service.name`, `le` ORDER BY fingerprint, ts) WINDOW rate_window AS (PARTITION BY fingerprint ORDER BY fingerprint, ts)), __spatial_aggregation_cte AS (SELECT ts, `service.name`, `le`, sum(per_series_value) AS value FROM __temporal_aggregation_cte WHERE isNaN(per_series_value) = ? GROUP BY ts, `service.name`, `le`) SELECT ts, `service.name`, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.950) AS value FROM __spatial_aggregation_cte GROUP BY `service.name`, ts ORDER BY `service.name`, ts",
|
||||||
Args: []any{"http_server_duration_bucket", uint64(1747936800000), uint64(1747983420000), "cumulative", false, "http_server_duration_bucket", uint64(1747947390000), uint64(1747983420000), 0},
|
Args: []any{"http_server_duration_bucket", uint64(1747936800000), uint64(1747983420000), "cumulative", false, "http_server_duration_bucket", uint64(1747947360000), uint64(1747983420000), 0},
|
||||||
},
|
},
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
},
|
},
|
||||||
|
|||||||
578
pkg/types/cloudintegrationtypes/cloudintegration.go
Normal file
578
pkg/types/cloudintegrationtypes/cloudintegration.go
Normal file
@@ -0,0 +1,578 @@
|
|||||||
|
// NOTE:
|
||||||
|
// - When Account keyword is used in struct names, it refers cloud integration account. CloudIntegration refers to DB schema.
|
||||||
|
// - When Account Config keyword is used in struct names, it refers to configuration for cloud integration accounts
|
||||||
|
// - When Service keyword is used in struct names, it refers to cloud integration service. CloudIntegrationService refers to DB schema.
|
||||||
|
// where `service` is services provided by each cloud provider like AWS S3, Azure BlobStorage etc.
|
||||||
|
// - When Service Config keyword is used in struct names, it refers to configuration for cloud integration services
|
||||||
|
package cloudintegrationtypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CloudProviderType type alias
|
||||||
|
type CloudProviderType struct{ valuer.String }
|
||||||
|
|
||||||
|
var (
|
||||||
|
CloudProviderTypeAWS = CloudProviderType{valuer.NewString("aws")}
|
||||||
|
CloudProviderTypeAzure = CloudProviderType{valuer.NewString("azure")}
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrCodeCloudProviderInvalidInput = errors.MustNewCode("invalid_cloud_provider")
|
||||||
|
|
||||||
|
// NewCloudProvider returns a new CloudProviderType from a string. It validates the input and returns an error if the input is not valid.
|
||||||
|
func NewCloudProvider(provider string) (CloudProviderType, error) {
|
||||||
|
switch provider {
|
||||||
|
case CloudProviderTypeAWS.StringValue():
|
||||||
|
return CloudProviderTypeAWS, nil
|
||||||
|
case CloudProviderTypeAzure.StringValue():
|
||||||
|
return CloudProviderTypeAzure, nil
|
||||||
|
default:
|
||||||
|
return CloudProviderType{}, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid cloud provider: %s", provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
AWSIntegrationUserEmail = valuer.MustNewEmail("aws-integration@signoz.io")
|
||||||
|
AzureIntegrationUserEmail = valuer.MustNewEmail("azure-integration@signoz.io")
|
||||||
|
)
|
||||||
|
|
||||||
|
// CloudIntegrationUserEmails is the list of valid emails for Cloud One Click integrations.
|
||||||
|
// This is used for validation and restrictions in different contexts, across codebase.
|
||||||
|
var CloudIntegrationUserEmails = []valuer.Email{
|
||||||
|
AWSIntegrationUserEmail,
|
||||||
|
AzureIntegrationUserEmail,
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsCloudIntegrationDashboardUuid(dashboardUuid string) bool {
|
||||||
|
parts := strings.SplitN(dashboardUuid, "--", 4)
|
||||||
|
if len(parts) != 4 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts[0] == "cloud-integration"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCloudIntegrationDashboardID returns the cloud provider from dashboard id, if it's a cloud integration dashboard id.
|
||||||
|
// throws an error if invalid format or invalid cloud provider is provided in the dashboard id.
|
||||||
|
func GetCloudProviderFromDashboardID(dashboardUuid string) (CloudProviderType, error) {
|
||||||
|
parts := strings.SplitN(dashboardUuid, "--", 4)
|
||||||
|
if len(parts) != 4 {
|
||||||
|
return CloudProviderType{}, errors.NewInvalidInputf(ErrCodeCloudProviderInvalidInput, "invalid dashboard uuid: %s", dashboardUuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
providerStr := parts[1]
|
||||||
|
|
||||||
|
cloudProvider, err := NewCloudProvider(providerStr)
|
||||||
|
if err != nil {
|
||||||
|
return CloudProviderType{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloudProvider, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic utility functions for JSON serialization/deserialization
|
||||||
|
// this is helpful to return right errors from a common place and avoid repeating the same code in multiple places.
|
||||||
|
// UnmarshalJSON is a generic function to unmarshal JSON data into any type
|
||||||
|
func UnmarshalJSON[T any](src []byte, target *T) error {
|
||||||
|
err := json.Unmarshal(src, target)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapInternalf(
|
||||||
|
err, errors.CodeInternal, "couldn't deserialize JSON",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON is a generic function to marshal any type to JSON
|
||||||
|
func MarshalJSON[T any](source *T) ([]byte, error) {
|
||||||
|
if source == nil {
|
||||||
|
return nil, errors.NewInternalf(errors.CodeInternal, "source is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
serialized, err := json.Marshal(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WrapInternalf(
|
||||||
|
err, errors.CodeInternal, "couldn't serialize to JSON",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return serialized, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableConnectedAccountsList is the response for listing connected accounts for a cloud provider.
|
||||||
|
type GettableConnectedAccountsList struct {
|
||||||
|
Accounts []*Account `json:"accounts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigNozAgentConfig represents parameters required for agent deployment in cloud provider accounts
|
||||||
|
// these represent parameters passed during agent deployment, how they are passed might change for each cloud provider but the purpose is same.
|
||||||
|
type SigNozAgentConfig struct {
|
||||||
|
Region string `json:"region,omitempty"` // AWS-specific: The region in which SigNoz agent should be installed
|
||||||
|
|
||||||
|
IngestionUrl string `json:"ingestion_url"`
|
||||||
|
IngestionKey string `json:"ingestion_key"`
|
||||||
|
SigNozAPIUrl string `json:"signoz_api_url"`
|
||||||
|
SigNozAPIKey string `json:"signoz_api_key"`
|
||||||
|
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostableConnectionArtifact represent request body for generating connection artifact API.
|
||||||
|
// Data is request body raw bytes since each cloud provider will have have different request body structure and generics hardly help in such cases.
|
||||||
|
// Artifact is a generic name for different types of connection methods like connection URL for AWS, connection command for Azure etc.
|
||||||
|
type PostableConnectionArtifact struct {
|
||||||
|
OrgID string
|
||||||
|
Data []byte // either PostableAWSConnectionUrl or PostableAzureConnectionCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostableAWSConnectionUrl is request body for AWS connection artifact API
|
||||||
|
type PostableAWSConnectionUrl struct {
|
||||||
|
AgentConfig *SigNozAgentConfig `json:"agent_config"`
|
||||||
|
AccountConfig *AWSAccountConfig `json:"account_config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostableAzureConnectionCommand is request body for Azure connection artifact API
|
||||||
|
type PostableAzureConnectionCommand struct {
|
||||||
|
AgentConfig *SigNozAgentConfig `json:"agent_config"`
|
||||||
|
AccountConfig *AzureAccountConfig `json:"account_config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableAzureConnectionArtifact is Azure specific connection artifact which contains connection commands for agent deployment
|
||||||
|
type GettableAzureConnectionArtifact struct {
|
||||||
|
AzureShellConnectionCommand string `json:"az_shell_connection_command"`
|
||||||
|
AzureCliConnectionCommand string `json:"az_cli_connection_command"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableAWSConnectionUrl is AWS specific connection artifact which contains connection url for agent deployment
|
||||||
|
type GettableAWSConnectionUrl struct {
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
ConnectionUrl string `json:"connection_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableAzureConnectionCommand is Azure specific connection artifact which contains connection commands for agent deployment
|
||||||
|
type GettableAzureConnectionCommand struct {
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
AzureShellConnectionCommand string `json:"az_shell_connection_command"`
|
||||||
|
AzureCliConnectionCommand string `json:"az_cli_connection_command"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableAccountStatus is cloud integration account status response
|
||||||
|
type GettableAccountStatus struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
CloudAccountId *string `json:"cloud_account_id,omitempty"`
|
||||||
|
Status AccountStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostableAgentCheckInPayload is request body for agent check-in API.
|
||||||
|
// This is used by agent to send heartbeat.
|
||||||
|
type PostableAgentCheckInPayload struct {
|
||||||
|
ID string `json:"account_id"`
|
||||||
|
AccountID string `json:"cloud_account_id"`
|
||||||
|
// Arbitrary cloud specific Agent data
|
||||||
|
Data map[string]any `json:"data,omitempty"`
|
||||||
|
OrgID string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWSAgentIntegrationConfig is used by agent for deploying infra to send telemetry to SigNoz
|
||||||
|
type AWSAgentIntegrationConfig struct {
|
||||||
|
EnabledRegions []string `json:"enabled_regions"`
|
||||||
|
TelemetryCollectionStrategy *AWSCollectionStrategy `json:"telemetry,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AzureAgentIntegrationConfig is used by agent for deploying infra to send telemetry to SigNoz
|
||||||
|
type AzureAgentIntegrationConfig struct {
|
||||||
|
DeploymentRegion string `json:"deployment_region"` // will not be changed once set
|
||||||
|
EnabledResourceGroups []string `json:"resource_groups"`
|
||||||
|
// TelemetryCollectionStrategy is map of service to telemetry config
|
||||||
|
TelemetryCollectionStrategy map[string]*AzureCollectionStrategy `json:"telemetry,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableAgentCheckInRes is generic response from agent check-in API.
|
||||||
|
// AWSAgentIntegrationConfig and AzureAgentIntegrationConfig these configs are used by agent to deploy the infra and send telemetry to SigNoz
|
||||||
|
type GettableAgentCheckInRes[AgentConfigT any] struct {
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
CloudAccountId string `json:"cloud_account_id"`
|
||||||
|
RemovedAt *time.Time `json:"removed_at"`
|
||||||
|
IntegrationConfig AgentConfigT `json:"integration_config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatableServiceConfig is generic
|
||||||
|
type UpdatableServiceConfig[ServiceConfigT any] struct {
|
||||||
|
CloudAccountId string `json:"cloud_account_id"`
|
||||||
|
Config ServiceConfigT `json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceConfigTyped is a generic interface for cloud integration service's configuration
|
||||||
|
// this is generic interface to define helper functions for CloudIntegrationService.Config field.
|
||||||
|
type ServiceConfigTyped[definition Definition] interface {
|
||||||
|
Validate(def definition) error
|
||||||
|
IsMetricsEnabled() bool
|
||||||
|
IsLogsEnabled() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type AWSServiceConfig struct {
|
||||||
|
Logs *AWSServiceLogsConfig `json:"logs,omitempty"`
|
||||||
|
Metrics *AWSServiceMetricsConfig `json:"metrics,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AWSServiceLogsConfig struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
S3Buckets map[string][]string `json:"s3_buckets,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AWSServiceMetricsConfig struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMetricsEnabled returns true if metrics collection is configured and enabled
|
||||||
|
func (a *AWSServiceConfig) IsMetricsEnabled() bool {
|
||||||
|
return a.Metrics != nil && a.Metrics.Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLogsEnabled returns true if logs collection is configured and enabled
|
||||||
|
func (a *AWSServiceConfig) IsLogsEnabled() bool {
|
||||||
|
return a.Logs != nil && a.Logs.Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
type AzureServiceConfig struct {
|
||||||
|
Logs []*AzureServiceLogsConfig `json:"logs,omitempty"`
|
||||||
|
Metrics []*AzureServiceMetricsConfig `json:"metrics,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AzureServiceLogsConfig is Azure specific service config for logs
|
||||||
|
type AzureServiceLogsConfig struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AzureServiceMetricsConfig is Azure specific service config for metrics
|
||||||
|
type AzureServiceMetricsConfig struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMetricsEnabled returns true if any metric is configured and enabled
|
||||||
|
func (a *AzureServiceConfig) IsMetricsEnabled() bool {
|
||||||
|
if a.Metrics == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, m := range a.Metrics {
|
||||||
|
if m.Enabled {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLogsEnabled returns true if any log is configured and enabled
|
||||||
|
func (a *AzureServiceConfig) IsLogsEnabled() bool {
|
||||||
|
if a.Logs == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, l := range a.Logs {
|
||||||
|
if l.Enabled {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AWSServiceConfig) Validate(def *AWSDefinition) error {
|
||||||
|
if def.Id != S3Sync.String() && a.Logs != nil && a.Logs.S3Buckets != nil {
|
||||||
|
return errors.NewInvalidInputf(errors.CodeInvalidInput, "s3 buckets can only be added to service-type[%s]", S3Sync)
|
||||||
|
} else if def.Id == S3Sync.String() && a.Logs != nil && a.Logs.S3Buckets != nil {
|
||||||
|
for region := range a.Logs.S3Buckets {
|
||||||
|
if _, found := ValidAWSRegions[region]; !found {
|
||||||
|
return errors.NewInvalidInputf(CodeInvalidCloudRegion, "invalid cloud region: %s", region)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AzureServiceConfig) Validate(def *AzureDefinition) error {
|
||||||
|
logsMap := make(map[string]bool)
|
||||||
|
metricsMap := make(map[string]bool)
|
||||||
|
|
||||||
|
if def.Strategy != nil && def.Strategy.Logs != nil {
|
||||||
|
for _, log := range def.Strategy.Logs {
|
||||||
|
logsMap[log.Name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if def.Strategy != nil && def.Strategy.Metrics != nil {
|
||||||
|
for _, metric := range def.Strategy.Metrics {
|
||||||
|
metricsMap[metric.Name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, log := range a.Logs {
|
||||||
|
if _, found := logsMap[log.Name]; !found {
|
||||||
|
return errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid log name: %s", log.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, metric := range a.Metrics {
|
||||||
|
if _, found := metricsMap[metric.Name]; !found {
|
||||||
|
return errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid metric name: %s", metric.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatableServiceConfigRes is response for UpdateServiceConfig API
|
||||||
|
// TODO: find a better way to name this
|
||||||
|
type UpdatableServiceConfigRes struct {
|
||||||
|
ServiceId string `json:"id"`
|
||||||
|
Config any `json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatableAccountConfigTyped is a generic struct for updating cloud integration account config used in UpdateAccountConfig API
|
||||||
|
type UpdatableAccountConfigTyped[AccountConfigT any] struct {
|
||||||
|
Config *AccountConfigT `json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
UpdatableAWSAccountConfig = UpdatableAccountConfigTyped[AWSAccountConfig]
|
||||||
|
UpdatableAzureAccountConfig = UpdatableAccountConfigTyped[AzureAccountConfig]
|
||||||
|
)
|
||||||
|
|
||||||
|
// AWSAccountConfig is the configuration for AWS cloud integration account
|
||||||
|
type AWSAccountConfig struct {
|
||||||
|
EnabledRegions []string `json:"regions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AzureAccountConfig is the configuration for Azure cloud integration account
|
||||||
|
type AzureAccountConfig struct {
|
||||||
|
DeploymentRegion string `json:"deployment_region,omitempty"`
|
||||||
|
EnabledResourceGroups []string `json:"resource_groups,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableServices is a generic struct for listing services of a cloud integration account used in ListServices API
|
||||||
|
type GettableServices[ServiceSummaryT any] struct {
|
||||||
|
Services []ServiceSummaryT `json:"services"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
GettableAWSServices = GettableServices[AWSServiceSummary]
|
||||||
|
GettableAzureServices = GettableServices[AzureServiceSummary]
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetServiceDetailsReq is a req struct for getting service definition details
|
||||||
|
type GetServiceDetailsReq struct {
|
||||||
|
OrgID valuer.UUID
|
||||||
|
ServiceId string
|
||||||
|
CloudAccountID *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceSummary is a generic struct for service summary used in ListServices API
|
||||||
|
type ServiceSummary[ServiceConfigT any] struct {
|
||||||
|
DefinitionMetadata
|
||||||
|
Config *ServiceConfigT `json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
AWSServiceSummary = ServiceSummary[AWSServiceConfig]
|
||||||
|
AzureServiceSummary = ServiceSummary[AzureServiceConfig]
|
||||||
|
)
|
||||||
|
|
||||||
|
// GettableServiceDetails is a generic struct for service details used in GetServiceDetails API
|
||||||
|
type GettableServiceDetails[DefinitionT any, ServiceConfigT any] struct {
|
||||||
|
Definition DefinitionT `json:",inline"`
|
||||||
|
Config ServiceConfigT `json:"config"`
|
||||||
|
ConnectionStatus *ServiceConnectionStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
GettableAWSServiceDetails = GettableServiceDetails[AWSDefinition, *AWSServiceConfig]
|
||||||
|
GettableAzureServiceDetails = GettableServiceDetails[AzureDefinition, *AzureServiceConfig]
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceConnectionStatus represents integration connection status for a particular service
|
||||||
|
// this struct helps to check ingested data and determines connection status by whether data was ingested or not.
|
||||||
|
// this is composite struct for both metrics and logs
|
||||||
|
type ServiceConnectionStatus struct {
|
||||||
|
Logs []*SignalConnectionStatus `json:"logs"`
|
||||||
|
Metrics []*SignalConnectionStatus `json:"metrics"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignalConnectionStatus represents connection status for a particular signal type (logs or metrics) for a service
|
||||||
|
// this struct is used in API responses for clients to show relevant information about the connection status.
|
||||||
|
type SignalConnectionStatus struct {
|
||||||
|
CategoryID string `json:"category"`
|
||||||
|
CategoryDisplayName string `json:"category_display_name"`
|
||||||
|
LastReceivedTsMillis int64 `json:"last_received_ts_ms"` // epoch milliseconds
|
||||||
|
LastReceivedFrom string `json:"last_received_from"` // resource identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableCloudIntegrationConnectionParams is response for connection params API
|
||||||
|
type GettableCloudIntegrationConnectionParams struct {
|
||||||
|
IngestionUrl string `json:"ingestion_url,omitempty"`
|
||||||
|
IngestionKey string `json:"ingestion_key,omitempty"`
|
||||||
|
SigNozAPIUrl string `json:"signoz_api_url,omitempty"`
|
||||||
|
SigNozAPIKey string `json:"signoz_api_key,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableIngestionKey is a struct for ingestion key returned from gateway
|
||||||
|
type GettableIngestionKey struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
// other attributes from gateway response not included here since they are not being used.
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableIngestionKeysSearch is a struct for response of ingestion keys search API on gateway
|
||||||
|
type GettableIngestionKeysSearch struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Data []GettableIngestionKey `json:"data"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableCreateIngestionKey is a struct for response of create ingestion key API on gateway
|
||||||
|
type GettableCreateIngestionKey struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Data GettableIngestionKey `json:"data"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GettableDeployment is response struct for deployment details fetched from Zeus
|
||||||
|
type GettableDeployment struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ClusterInfo struct {
|
||||||
|
Region struct {
|
||||||
|
DNS string `json:"dns"`
|
||||||
|
} `json:"region"`
|
||||||
|
} `json:"cluster"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// DATABASE TYPES
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// Cloud integration uses the cloud_integration table
|
||||||
|
// and cloud_integrations_service table
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type CloudIntegration struct {
|
||||||
|
bun.BaseModel `bun:"table:cloud_integration"`
|
||||||
|
|
||||||
|
types.Identifiable
|
||||||
|
types.TimeAuditable
|
||||||
|
Provider string `json:"provider" bun:"provider,type:text,unique:provider_id"`
|
||||||
|
Config string `json:"config" bun:"config,type:text"` // json serialized config
|
||||||
|
AccountID *string `json:"account_id" bun:"account_id,type:text"`
|
||||||
|
LastAgentReport *AgentReport `json:"last_agent_report" bun:"last_agent_report,type:text"`
|
||||||
|
RemovedAt *time.Time `json:"removed_at" bun:"removed_at,type:timestamp,nullzero"`
|
||||||
|
OrgID string `bun:"org_id,type:text,unique:provider_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account represents a cloud integration account, this is used for business logic and API responses.
|
||||||
|
type Account struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
CloudAccountId string `json:"cloud_account_id"`
|
||||||
|
Config any `json:"config"` // AWSAccountConfig or AzureAccountConfig
|
||||||
|
Status AccountStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountStatus is generic struct for cloud integration account status
|
||||||
|
type AccountStatus struct {
|
||||||
|
Integration AccountIntegrationStatus `json:"integration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountIntegrationStatus stores heartbeat information from agent check in
|
||||||
|
type AccountIntegrationStatus struct {
|
||||||
|
LastHeartbeatTsMillis *int64 `json:"last_heartbeat_ts_ms"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *CloudIntegration) Status() AccountStatus {
|
||||||
|
status := AccountStatus{}
|
||||||
|
if a.LastAgentReport != nil {
|
||||||
|
lastHeartbeat := a.LastAgentReport.TimestampMillis
|
||||||
|
status.Integration.LastHeartbeatTsMillis = &lastHeartbeat
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *CloudIntegration) Account(cloudProvider CloudProviderType) *Account {
|
||||||
|
ca := &Account{Id: a.ID.StringValue(), Status: a.Status()}
|
||||||
|
|
||||||
|
if a.AccountID != nil {
|
||||||
|
ca.CloudAccountId = *a.AccountID
|
||||||
|
}
|
||||||
|
|
||||||
|
ca.Config = map[string]interface{}{}
|
||||||
|
|
||||||
|
if len(a.Config) < 1 {
|
||||||
|
return ca
|
||||||
|
}
|
||||||
|
|
||||||
|
switch cloudProvider {
|
||||||
|
case CloudProviderTypeAWS:
|
||||||
|
config := new(AWSAccountConfig)
|
||||||
|
_ = UnmarshalJSON([]byte(a.Config), config)
|
||||||
|
ca.Config = config
|
||||||
|
case CloudProviderTypeAzure:
|
||||||
|
config := new(AzureAccountConfig)
|
||||||
|
_ = UnmarshalJSON([]byte(a.Config), config)
|
||||||
|
ca.Config = config
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return ca
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentReport struct {
|
||||||
|
TimestampMillis int64 `json:"timestamp_millis"`
|
||||||
|
Data map[string]any `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan scans data from db
|
||||||
|
func (r *AgentReport) Scan(src any) error {
|
||||||
|
var data []byte
|
||||||
|
switch v := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
data = v
|
||||||
|
case string:
|
||||||
|
data = []byte(v)
|
||||||
|
default:
|
||||||
|
return errors.NewInternalf(errors.CodeInternal, "tried to scan from %T instead of string or bytes", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Unmarshal(data, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value serializes data to bytes for db insertion
|
||||||
|
func (r *AgentReport) Value() (driver.Value, error) {
|
||||||
|
if r == nil {
|
||||||
|
return nil, errors.NewInternalf(errors.CodeInternal, "agent report is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
serialized, err := json.Marshal(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WrapInternalf(
|
||||||
|
err, errors.CodeInternal, "couldn't serialize agent report to JSON",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return serialized, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudIntegrationService struct {
|
||||||
|
bun.BaseModel `bun:"table:cloud_integration_service,alias:cis"`
|
||||||
|
|
||||||
|
types.Identifiable
|
||||||
|
types.TimeAuditable
|
||||||
|
Type string `bun:"type,type:text,notnull,unique:cloud_integration_id_type"`
|
||||||
|
Config string `bun:"config,type:text"` // json serialized config
|
||||||
|
CloudIntegrationID string `bun:"cloud_integration_id,type:text,notnull,unique:cloud_integration_id_type,references:cloud_integrations(id),on_delete:cascade"`
|
||||||
|
}
|
||||||
103
pkg/types/cloudintegrationtypes/regions.go
Normal file
103
pkg/types/cloudintegrationtypes/regions.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package cloudintegrationtypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
CodeInvalidCloudRegion = errors.MustNewCode("invalid_cloud_region")
|
||||||
|
CodeMismatchCloudProvider = errors.MustNewCode("cloud_provider_mismatch")
|
||||||
|
)
|
||||||
|
|
||||||
|
// List of all valid cloud regions on Amazon Web Services
|
||||||
|
var ValidAWSRegions = map[string]bool{
|
||||||
|
"af-south-1": true, // Africa (Cape Town).
|
||||||
|
"ap-east-1": true, // Asia Pacific (Hong Kong).
|
||||||
|
"ap-northeast-1": true, // Asia Pacific (Tokyo).
|
||||||
|
"ap-northeast-2": true, // Asia Pacific (Seoul).
|
||||||
|
"ap-northeast-3": true, // Asia Pacific (Osaka).
|
||||||
|
"ap-south-1": true, // Asia Pacific (Mumbai).
|
||||||
|
"ap-south-2": true, // Asia Pacific (Hyderabad).
|
||||||
|
"ap-southeast-1": true, // Asia Pacific (Singapore).
|
||||||
|
"ap-southeast-2": true, // Asia Pacific (Sydney).
|
||||||
|
"ap-southeast-3": true, // Asia Pacific (Jakarta).
|
||||||
|
"ap-southeast-4": true, // Asia Pacific (Melbourne).
|
||||||
|
"ca-central-1": true, // Canada (Central).
|
||||||
|
"ca-west-1": true, // Canada West (Calgary).
|
||||||
|
"eu-central-1": true, // Europe (Frankfurt).
|
||||||
|
"eu-central-2": true, // Europe (Zurich).
|
||||||
|
"eu-north-1": true, // Europe (Stockholm).
|
||||||
|
"eu-south-1": true, // Europe (Milan).
|
||||||
|
"eu-south-2": true, // Europe (Spain).
|
||||||
|
"eu-west-1": true, // Europe (Ireland).
|
||||||
|
"eu-west-2": true, // Europe (London).
|
||||||
|
"eu-west-3": true, // Europe (Paris).
|
||||||
|
"il-central-1": true, // Israel (Tel Aviv).
|
||||||
|
"me-central-1": true, // Middle East (UAE).
|
||||||
|
"me-south-1": true, // Middle East (Bahrain).
|
||||||
|
"sa-east-1": true, // South America (Sao Paulo).
|
||||||
|
"us-east-1": true, // US East (N. Virginia).
|
||||||
|
"us-east-2": true, // US East (Ohio).
|
||||||
|
"us-west-1": true, // US West (N. California).
|
||||||
|
"us-west-2": true, // US West (Oregon).
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of all valid cloud regions for Microsoft Azure
|
||||||
|
var ValidAzureRegions = map[string]bool{
|
||||||
|
"australiacentral": true, // Australia Central
|
||||||
|
"australiacentral2": true, // Australia Central 2
|
||||||
|
"australiaeast": true, // Australia East
|
||||||
|
"australiasoutheast": true, // Australia Southeast
|
||||||
|
"austriaeast": true, // Austria East
|
||||||
|
"belgiumcentral": true, // Belgium Central
|
||||||
|
"brazilsouth": true, // Brazil South
|
||||||
|
"brazilsoutheast": true, // Brazil Southeast
|
||||||
|
"canadacentral": true, // Canada Central
|
||||||
|
"canadaeast": true, // Canada East
|
||||||
|
"centralindia": true, // Central India
|
||||||
|
"centralus": true, // Central US
|
||||||
|
"chilecentral": true, // Chile Central
|
||||||
|
"denmarkeast": true, // Denmark East
|
||||||
|
"eastasia": true, // East Asia
|
||||||
|
"eastus": true, // East US
|
||||||
|
"eastus2": true, // East US 2
|
||||||
|
"francecentral": true, // France Central
|
||||||
|
"francesouth": true, // France South
|
||||||
|
"germanynorth": true, // Germany North
|
||||||
|
"germanywestcentral": true, // Germany West Central
|
||||||
|
"indonesiacentral": true, // Indonesia Central
|
||||||
|
"israelcentral": true, // Israel Central
|
||||||
|
"italynorth": true, // Italy North
|
||||||
|
"japaneast": true, // Japan East
|
||||||
|
"japanwest": true, // Japan West
|
||||||
|
"koreacentral": true, // Korea Central
|
||||||
|
"koreasouth": true, // Korea South
|
||||||
|
"malaysiawest": true, // Malaysia West
|
||||||
|
"mexicocentral": true, // Mexico Central
|
||||||
|
"newzealandnorth": true, // New Zealand North
|
||||||
|
"northcentralus": true, // North Central US
|
||||||
|
"northeurope": true, // North Europe
|
||||||
|
"norwayeast": true, // Norway East
|
||||||
|
"norwaywest": true, // Norway West
|
||||||
|
"polandcentral": true, // Poland Central
|
||||||
|
"qatarcentral": true, // Qatar Central
|
||||||
|
"southafricanorth": true, // South Africa North
|
||||||
|
"southafricawest": true, // South Africa West
|
||||||
|
"southcentralus": true, // South Central US
|
||||||
|
"southindia": true, // South India
|
||||||
|
"southeastasia": true, // Southeast Asia
|
||||||
|
"spaincentral": true, // Spain Central
|
||||||
|
"swedencentral": true, // Sweden Central
|
||||||
|
"switzerlandnorth": true, // Switzerland North
|
||||||
|
"switzerlandwest": true, // Switzerland West
|
||||||
|
"uaecentral": true, // UAE Central
|
||||||
|
"uaenorth": true, // UAE North
|
||||||
|
"uksouth": true, // UK South
|
||||||
|
"ukwest": true, // UK West
|
||||||
|
"westcentralus": true, // West Central US
|
||||||
|
"westeurope": true, // West Europe
|
||||||
|
"westindia": true, // West India
|
||||||
|
"westus": true, // West US
|
||||||
|
"westus2": true, // West US 2
|
||||||
|
"westus3": true, // West US 3
|
||||||
|
}
|
||||||
263
pkg/types/cloudintegrationtypes/servicedefinitions.go
Normal file
263
pkg/types/cloudintegrationtypes/servicedefinitions.go
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
package cloudintegrationtypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/dashboardtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var S3Sync = valuer.NewString("s3sync")
|
||||||
|
|
||||||
|
// Generic interface for cloud service definition.
|
||||||
|
// This is implemented by AWSDefinition and AzureDefinition, which represent service definitions for AWS and Azure respectively.
|
||||||
|
// Generics work well so far because service definitions share a similar logic.
|
||||||
|
// We dont want to over-do generics as well, if the service definitions functionally diverge in the future consider breaking generics.
|
||||||
|
type Definition interface {
|
||||||
|
GetId() string
|
||||||
|
Validate() error
|
||||||
|
PopulateDashboardURLs(cloudProvider CloudProviderType, svcId string)
|
||||||
|
GetIngestionStatusCheck() *IngestionStatusCheck
|
||||||
|
GetAssets() Assets
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWSDefinition represents AWS Service definition, which includes collection strategy, dashboards and meta info for integration
|
||||||
|
type AWSDefinition = ServiceDefinition[AWSCollectionStrategy]
|
||||||
|
|
||||||
|
// AzureDefinition represents Azure Service definition, which includes collection strategy, dashboards and meta info for integration
|
||||||
|
type AzureDefinition = ServiceDefinition[AzureCollectionStrategy]
|
||||||
|
|
||||||
|
// Making AWSDefinition and AzureDefinition satisfy Definition interface, so that they can be used in a generic way
|
||||||
|
var (
|
||||||
|
_ Definition = &AWSDefinition{}
|
||||||
|
_ Definition = &AzureDefinition{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceDefinition represents generic struct for cloud service, regardless of the cloud provider.
|
||||||
|
// this struct must satify Definition interface.
|
||||||
|
// StrategyT is of either AWSCollectionStrategy or AzureCollectionStrategy, depending on the cloud provider.
|
||||||
|
type ServiceDefinition[StrategyT any] struct {
|
||||||
|
DefinitionMetadata
|
||||||
|
Overview string `json:"overview"` // markdown
|
||||||
|
Assets Assets `json:"assets"`
|
||||||
|
SupportedSignals SupportedSignals `json:"supported_signals"`
|
||||||
|
DataCollected DataCollected `json:"data_collected"`
|
||||||
|
IngestionStatusCheck *IngestionStatusCheck `json:"ingestion_status_check,omitempty"`
|
||||||
|
Strategy *StrategyT `json:"telemetry_collection_strategy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Following methods are quite self explanatory, they are just to satisfy the Definition interface and provide some utility functions for service definitions.
|
||||||
|
func (def *ServiceDefinition[StrategyT]) GetId() string {
|
||||||
|
return def.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (def *ServiceDefinition[StrategyT]) Validate() error {
|
||||||
|
seenDashboardIds := map[string]interface{}{}
|
||||||
|
|
||||||
|
if def.Strategy == nil {
|
||||||
|
return errors.NewInternalf(errors.CodeInternal, "telemetry_collection_strategy is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dd := range def.Assets.Dashboards {
|
||||||
|
if _, seen := seenDashboardIds[dd.Id]; seen {
|
||||||
|
return errors.NewInternalf(errors.CodeInternal, "multiple dashboards found with id %s", dd.Id)
|
||||||
|
}
|
||||||
|
seenDashboardIds[dd.Id] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (def *ServiceDefinition[StrategyT]) PopulateDashboardURLs(cloudProvider CloudProviderType, svcId string) {
|
||||||
|
for i := range def.Assets.Dashboards {
|
||||||
|
dashboardId := def.Assets.Dashboards[i].Id
|
||||||
|
url := "/dashboard/" + GetCloudIntegrationDashboardID(cloudProvider, svcId, dashboardId)
|
||||||
|
def.Assets.Dashboards[i].Url = url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (def *ServiceDefinition[StrategyT]) GetIngestionStatusCheck() *IngestionStatusCheck {
|
||||||
|
return def.IngestionStatusCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
func (def *ServiceDefinition[StrategyT]) GetAssets() Assets {
|
||||||
|
return def.Assets
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefinitionMetadata represents service definition metadata. This is useful for showing service overview
|
||||||
|
type DefinitionMetadata struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IngestionStatusCheckCategory represents a category of ingestion status check. Applies for both metrics and logs.
|
||||||
|
// A category can be "Overview" of metrics or "Enhanced" Metrics for AWS, and "Transaction" or "Capacity" metrics for Azure.
|
||||||
|
// Each category can have multiple checks (AND logic), if all checks pass,
|
||||||
|
// then we can be sure that data is being ingested for that category of the signal
|
||||||
|
type IngestionStatusCheckCategory struct {
|
||||||
|
Category string `json:"category"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
Checks []*IngestionStatusCheckAttribute `json:"checks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IngestionStatusCheckAttribute represents a check or condition for ingestion status.
|
||||||
|
// Key can be metric name or part of log message
|
||||||
|
type IngestionStatusCheckAttribute struct {
|
||||||
|
Key string `json:"key"` // OPTIONAL search key (metric name or log message)
|
||||||
|
Attributes []*IngestionStatusCheckAttributeFilter `json:"attributes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IngestionStatusCheck represents combined checks for metrics and logs for a service
|
||||||
|
type IngestionStatusCheck struct {
|
||||||
|
Metrics []*IngestionStatusCheckCategory `json:"metrics"`
|
||||||
|
Logs []*IngestionStatusCheckCategory `json:"logs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IngestionStatusCheckAttributeFilter represents filter for a check, which can be used to filter specific log messages or metrics with specific attributes.
|
||||||
|
// For example, we can use it to filter logs with specific log level or metrics with specific dimensions.
|
||||||
|
type IngestionStatusCheckAttributeFilter struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Operator string `json:"operator"`
|
||||||
|
Value string `json:"value"` // OPTIONAL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assets represents the collection of dashboards
|
||||||
|
type Assets struct {
|
||||||
|
Dashboards []Dashboard `json:"dashboards"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportedSignals for cloud provider's service
|
||||||
|
type SupportedSignals struct {
|
||||||
|
Logs bool `json:"logs"`
|
||||||
|
Metrics bool `json:"metrics"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataCollected is curated static list of metrics and logs, this is shown as part of service overview
|
||||||
|
type DataCollected struct {
|
||||||
|
Logs []CollectedLogAttribute `json:"logs"`
|
||||||
|
Metrics []CollectedMetric `json:"metrics"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectedLogAttribute represents a log attribute that is present in all log entries for a service,
|
||||||
|
// this is shown as part of service overview
|
||||||
|
type CollectedLogAttribute struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectedMetric represents a metric that is collected for a service, this is shown as part of service overview
|
||||||
|
type CollectedMetric struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Unit string `json:"unit"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWSCollectionStrategy represents signal collection strategy for AWS services.
|
||||||
|
// this is AWS specific.
|
||||||
|
type AWSCollectionStrategy struct {
|
||||||
|
Metrics *AWSMetricsStrategy `json:"aws_metrics,omitempty"`
|
||||||
|
Logs *AWSLogsStrategy `json:"aws_logs,omitempty"`
|
||||||
|
S3Buckets map[string][]string `json:"s3_buckets,omitempty"` // Only available in S3 Sync Service Type in AWS
|
||||||
|
}
|
||||||
|
|
||||||
|
// AzureCollectionStrategy represents signal collection strategy for Azure services.
|
||||||
|
// this is Azure specific.
|
||||||
|
type AzureCollectionStrategy struct {
|
||||||
|
Metrics []*AzureMetricsStrategy `json:"azure_metrics,omitempty"`
|
||||||
|
Logs []*AzureLogsStrategy `json:"azure_logs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWSMetricsStrategy represents metrics collection strategy for AWS services.
|
||||||
|
// this is AWS specific.
|
||||||
|
type AWSMetricsStrategy struct {
|
||||||
|
// to be used as https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-includefilters
|
||||||
|
StreamFilters []struct {
|
||||||
|
// json tags here are in the shape expected by AWS API as detailed at
|
||||||
|
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-metricstream-metricstreamfilter.html
|
||||||
|
Namespace string `json:"Namespace"`
|
||||||
|
MetricNames []string `json:"MetricNames,omitempty"`
|
||||||
|
} `json:"cloudwatch_metric_stream_filters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWSLogsStrategy represents logs collection strategy for AWS services.
|
||||||
|
// this is AWS specific.
|
||||||
|
type AWSLogsStrategy struct {
|
||||||
|
Subscriptions []struct {
|
||||||
|
// subscribe to all logs groups with specified prefix.
|
||||||
|
// eg: `/aws/rds/`
|
||||||
|
LogGroupNamePrefix string `json:"log_group_name_prefix"`
|
||||||
|
|
||||||
|
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html
|
||||||
|
// "" implies no filtering is required.
|
||||||
|
FilterPattern string `json:"filter_pattern"`
|
||||||
|
} `json:"cloudwatch_logs_subscriptions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AzureMetricsStrategy represents metrics collection strategy for Azure services.
|
||||||
|
// this is Azure specific.
|
||||||
|
type AzureMetricsStrategy struct {
|
||||||
|
CategoryType string `json:"category_type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AzureLogsStrategy represents logs collection strategy for Azure services.
|
||||||
|
// this is Azure specific. Even though this is similar to AzureMetricsStrategy, keeping it separate for future flexibility and clarity.
|
||||||
|
type AzureLogsStrategy struct {
|
||||||
|
CategoryType string `json:"category_type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dashboard represents a dashboard definition for cloud integration.
|
||||||
|
type Dashboard struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
Definition *dashboardtypes.StorableDashboardData `json:"definition,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTILS
|
||||||
|
|
||||||
|
// GetCloudIntegrationDashboardID returns the dashboard id for a cloud integration, given the cloud provider, service id, and dashboard id.
|
||||||
|
// This is used to generate unique dashboard ids for cloud integration, and also to parse the dashboard id to get the cloud provider and service id when needed.
|
||||||
|
func GetCloudIntegrationDashboardID(cloudProvider CloudProviderType, svcId, dashboardId string) string {
|
||||||
|
return fmt.Sprintf("cloud-integration--%s--%s--%s", cloudProvider, svcId, dashboardId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDashboardsFromAssets returns the list of dashboards for the cloud provider service from definition
|
||||||
|
func GetDashboardsFromAssets(
|
||||||
|
svcId string,
|
||||||
|
orgID valuer.UUID,
|
||||||
|
cloudProvider CloudProviderType,
|
||||||
|
createdAt *time.Time,
|
||||||
|
assets Assets,
|
||||||
|
) []*dashboardtypes.Dashboard {
|
||||||
|
dashboards := make([]*dashboardtypes.Dashboard, 0)
|
||||||
|
|
||||||
|
for _, d := range assets.Dashboards {
|
||||||
|
author := fmt.Sprintf("%s-integration", cloudProvider)
|
||||||
|
dashboards = append(dashboards, &dashboardtypes.Dashboard{
|
||||||
|
ID: GetCloudIntegrationDashboardID(cloudProvider, svcId, d.Id),
|
||||||
|
Locked: true,
|
||||||
|
OrgID: orgID,
|
||||||
|
Data: *d.Definition,
|
||||||
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: *createdAt,
|
||||||
|
UpdatedAt: *createdAt,
|
||||||
|
},
|
||||||
|
UserAuditable: types.UserAuditable{
|
||||||
|
CreatedBy: author,
|
||||||
|
UpdatedBy: author,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return dashboards
|
||||||
|
}
|
||||||
42
pkg/types/cloudintegrationtypes/store.go
Normal file
42
pkg/types/cloudintegrationtypes/store.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package cloudintegrationtypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CloudIntegrationAccountStore interface {
|
||||||
|
ListConnected(ctx context.Context, orgId string, provider string) ([]CloudIntegration, error)
|
||||||
|
|
||||||
|
Get(ctx context.Context, orgId string, provider string, id string) (*CloudIntegration, error)
|
||||||
|
|
||||||
|
GetConnectedCloudAccount(ctx context.Context, orgId, provider string, accountID string) (*CloudIntegration, error)
|
||||||
|
|
||||||
|
// Insert an account or update it by (cloudProvider, id)
|
||||||
|
// for specified non-empty fields
|
||||||
|
Upsert(
|
||||||
|
ctx context.Context,
|
||||||
|
orgId string,
|
||||||
|
provider string,
|
||||||
|
id *string,
|
||||||
|
config []byte,
|
||||||
|
accountId *string,
|
||||||
|
agentReport *AgentReport,
|
||||||
|
removedAt *time.Time,
|
||||||
|
) (*CloudIntegration, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudIntegrationServiceStore interface {
|
||||||
|
Get(ctx context.Context, orgID, cloudAccountId, serviceType string) ([]byte, error)
|
||||||
|
|
||||||
|
Upsert(
|
||||||
|
ctx context.Context,
|
||||||
|
orgID,
|
||||||
|
cloudProvider,
|
||||||
|
cloudAccountId,
|
||||||
|
serviceId string,
|
||||||
|
config []byte,
|
||||||
|
) ([]byte, error)
|
||||||
|
|
||||||
|
GetAllForAccount(ctx context.Context, orgID, cloudAccountId string) (map[string][]byte, error)
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ var (
|
|||||||
var (
|
var (
|
||||||
TemplateNameInvitationEmail = TemplateName{valuer.NewString("invitation")}
|
TemplateNameInvitationEmail = TemplateName{valuer.NewString("invitation")}
|
||||||
TemplateNameResetPassword = TemplateName{valuer.NewString("reset_password")}
|
TemplateNameResetPassword = TemplateName{valuer.NewString("reset_password")}
|
||||||
|
TemplateNameAPIKeyEvent = TemplateName{valuer.NewString("api_key_event")}
|
||||||
)
|
)
|
||||||
|
|
||||||
type TemplateName struct{ valuer.String }
|
type TemplateName struct{ valuer.String }
|
||||||
@@ -28,6 +29,8 @@ func NewTemplateName(name string) (TemplateName, error) {
|
|||||||
return TemplateNameInvitationEmail, nil
|
return TemplateNameInvitationEmail, nil
|
||||||
case TemplateNameResetPassword.StringValue():
|
case TemplateNameResetPassword.StringValue():
|
||||||
return TemplateNameResetPassword, nil
|
return TemplateNameResetPassword, nil
|
||||||
|
case TemplateNameAPIKeyEvent.StringValue():
|
||||||
|
return TemplateNameAPIKeyEvent, nil
|
||||||
default:
|
default:
|
||||||
return TemplateName{}, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid template name: %s", name)
|
return TemplateName{}, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid template name: %s", name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package metrictypes
|
|||||||
import (
|
import (
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/errors"
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
@@ -135,6 +136,10 @@ func (t *Type) Scan(src interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t Type) IsPercentileSpaceAggregationAllowed() bool {
|
||||||
|
return t == HistogramType || t == ExpHistogramType || t == SummaryType
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
GaugeType = Type{valuer.NewString("gauge")}
|
GaugeType = Type{valuer.NewString("gauge")}
|
||||||
SumType = Type{valuer.NewString("sum")}
|
SumType = Type{valuer.NewString("sum")}
|
||||||
@@ -185,6 +190,10 @@ func (TimeAggregation) Enum() []any {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t TimeAggregation) IsValid() bool {
|
||||||
|
return slices.ContainsFunc(t.Enum(), func(v any) bool { return v == t })
|
||||||
|
}
|
||||||
|
|
||||||
type SpaceAggregation struct {
|
type SpaceAggregation struct {
|
||||||
valuer.String
|
valuer.String
|
||||||
}
|
}
|
||||||
@@ -218,6 +227,10 @@ func (SpaceAggregation) Enum() []any {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s SpaceAggregation) IsValid() bool {
|
||||||
|
return slices.ContainsFunc(s.Enum(), func(v any) bool { return v == s })
|
||||||
|
}
|
||||||
|
|
||||||
func (s SpaceAggregation) IsPercentile() bool {
|
func (s SpaceAggregation) IsPercentile() bool {
|
||||||
return s == SpaceAggregationPercentile50 ||
|
return s == SpaceAggregationPercentile50 ||
|
||||||
s == SpaceAggregationPercentile75 ||
|
s == SpaceAggregationPercentile75 ||
|
||||||
|
|||||||
@@ -215,6 +215,13 @@ func (q *QueryBuilderQuery[T]) validateAggregations(requestType RequestType) err
|
|||||||
aggId,
|
aggId,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if !v.SpaceAggregation.IsValid() {
|
||||||
|
return errors.Newf(
|
||||||
|
errors.TypeInvalidInput,
|
||||||
|
errors.CodeInvalidInput,
|
||||||
|
"invalid space aggregation, should be one of the following: [`sum`, `avg`, `min`, `max`, `count`, `p50`, `p75`, `p90`, `p95`, `p99`]",
|
||||||
|
)
|
||||||
|
}
|
||||||
case TraceAggregation:
|
case TraceAggregation:
|
||||||
if v.Expression == "" {
|
if v.Expression == "" {
|
||||||
aggId := fmt.Sprintf("aggregation #%d", i+1)
|
aggId := fmt.Sprintf("aggregation #%d", i+1)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type Store interface {
|
|||||||
GetByOrgIDAndName(context.Context, valuer.UUID, string) (*StorableRole, error)
|
GetByOrgIDAndName(context.Context, valuer.UUID, string) (*StorableRole, error)
|
||||||
List(context.Context, valuer.UUID) ([]*StorableRole, error)
|
List(context.Context, valuer.UUID) ([]*StorableRole, error)
|
||||||
ListByOrgIDAndNames(context.Context, valuer.UUID, []string) ([]*StorableRole, error)
|
ListByOrgIDAndNames(context.Context, valuer.UUID, []string) ([]*StorableRole, error)
|
||||||
|
ListByOrgIDAndIDs(context.Context, valuer.UUID, []valuer.UUID) ([]*StorableRole, error)
|
||||||
Update(context.Context, valuer.UUID, *StorableRole) error
|
Update(context.Context, valuer.UUID, *StorableRole) error
|
||||||
Delete(context.Context, valuer.UUID, valuer.UUID) error
|
Delete(context.Context, valuer.UUID, valuer.UUID) error
|
||||||
RunInTx(context.Context, func(ctx context.Context) error) error
|
RunInTx(context.Context, func(ctx context.Context) error) error
|
||||||
|
|||||||
161
pkg/types/serviceaccounttypes/factor_api_key.go
Normal file
161
pkg/types/serviceaccounttypes/factor_api_key.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package serviceaccounttypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCodeServiceAccountFactorAPIkeyInvalidInput = errors.MustNewCode("service_account_factor_api_key_invalid_input")
|
||||||
|
ErrCodeServiceAccountFactorAPIKeyAlreadyExists = errors.MustNewCode("service_account_factor_api_key_already_exists")
|
||||||
|
ErrCodeServiceAccounFactorAPIKeytNotFound = errors.MustNewCode("service_account_factor_api_key_not_found")
|
||||||
|
)
|
||||||
|
|
||||||
|
type StorableFactorAPIKey struct {
|
||||||
|
bun.BaseModel `bun:"table:factor_api_key"`
|
||||||
|
|
||||||
|
types.Identifiable
|
||||||
|
types.TimeAuditable
|
||||||
|
Name string `bun:"name"`
|
||||||
|
Key string `bun:"key"`
|
||||||
|
ExpiresAt uint64 `bun:"expires_at"`
|
||||||
|
LastUsed time.Time `bun:"last_used"`
|
||||||
|
ServiceAccountID string `bun:"service_account_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FactorAPIKey struct {
|
||||||
|
types.Identifiable
|
||||||
|
types.TimeAuditable
|
||||||
|
Name string `json:"name" requrired:"true"`
|
||||||
|
Key string `json:"key" required:"true"`
|
||||||
|
ExpiresAt uint64 `json:"expires_at" required:"true"`
|
||||||
|
LastUsed time.Time `json:"last_used" required:"true"`
|
||||||
|
ServiceAccountID valuer.UUID `json:"service_account_id" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GettableFactorAPIKeyWithKey struct {
|
||||||
|
types.Identifiable
|
||||||
|
Key string `json:"key" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GettableFactorAPIKey struct {
|
||||||
|
types.Identifiable
|
||||||
|
types.TimeAuditable
|
||||||
|
Name string `json:"name" requrired:"true"`
|
||||||
|
ExpiresAt uint64 `json:"expires_at" required:"true"`
|
||||||
|
LastUsed time.Time `json:"last_used" required:"true"`
|
||||||
|
ServiceAccountID valuer.UUID `json:"service_account_id" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostableFactorAPIKey struct {
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
ExpiresAt uint64 `json:"expires_at" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdatableFactorAPIKey struct {
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
ExpiresAt uint64 `json:"expires_at" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactorAPIKeyFromStorable(storable *StorableFactorAPIKey) *FactorAPIKey {
|
||||||
|
return &FactorAPIKey{
|
||||||
|
Identifiable: storable.Identifiable,
|
||||||
|
TimeAuditable: storable.TimeAuditable,
|
||||||
|
Name: storable.Name,
|
||||||
|
Key: storable.Key,
|
||||||
|
ExpiresAt: storable.ExpiresAt,
|
||||||
|
LastUsed: storable.LastUsed,
|
||||||
|
ServiceAccountID: valuer.MustNewUUID(storable.ServiceAccountID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactorAPIKeyFromStorables(storables []*StorableFactorAPIKey) []*FactorAPIKey {
|
||||||
|
factorAPIKeys := make([]*FactorAPIKey, len(storables))
|
||||||
|
|
||||||
|
for idx, storable := range storables {
|
||||||
|
factorAPIKeys[idx] = NewFactorAPIKeyFromStorable(storable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return factorAPIKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStorableFactorAPIKey(factorAPIKey *FactorAPIKey) *StorableFactorAPIKey {
|
||||||
|
return &StorableFactorAPIKey{
|
||||||
|
Identifiable: factorAPIKey.Identifiable,
|
||||||
|
TimeAuditable: factorAPIKey.TimeAuditable,
|
||||||
|
Name: factorAPIKey.Name,
|
||||||
|
Key: factorAPIKey.Key,
|
||||||
|
ExpiresAt: factorAPIKey.ExpiresAt,
|
||||||
|
LastUsed: factorAPIKey.LastUsed,
|
||||||
|
ServiceAccountID: factorAPIKey.ServiceAccountID.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGettableFactorAPIKeys(keys []*FactorAPIKey) []*GettableFactorAPIKey {
|
||||||
|
gettables := make([]*GettableFactorAPIKey, len(keys))
|
||||||
|
|
||||||
|
for idx, key := range keys {
|
||||||
|
gettables[idx] = &GettableFactorAPIKey{
|
||||||
|
Identifiable: key.Identifiable,
|
||||||
|
TimeAuditable: key.TimeAuditable,
|
||||||
|
Name: key.Name,
|
||||||
|
ExpiresAt: key.ExpiresAt,
|
||||||
|
LastUsed: key.LastUsed,
|
||||||
|
ServiceAccountID: key.ServiceAccountID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gettables
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGettableFactorAPIKeyWithKey(id valuer.UUID, key string) *GettableFactorAPIKeyWithKey {
|
||||||
|
return &GettableFactorAPIKeyWithKey{
|
||||||
|
Identifiable: types.Identifiable{
|
||||||
|
ID: id,
|
||||||
|
},
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (apiKey *FactorAPIKey) Update(name string, expiresAt uint64) {
|
||||||
|
apiKey.Name = name
|
||||||
|
apiKey.ExpiresAt = expiresAt
|
||||||
|
apiKey.UpdatedAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key *PostableFactorAPIKey) UnmarshalJSON(data []byte) error {
|
||||||
|
type Alias PostableFactorAPIKey
|
||||||
|
|
||||||
|
var temp Alias
|
||||||
|
if err := json.Unmarshal(data, &temp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if temp.Name == "" {
|
||||||
|
return errors.New(errors.TypeInvalidInput, ErrCodeServiceAccountFactorAPIkeyInvalidInput, "name cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
*key = PostableFactorAPIKey(temp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key *UpdatableFactorAPIKey) UnmarshalJSON(data []byte) error {
|
||||||
|
type Alias UpdatableFactorAPIKey
|
||||||
|
|
||||||
|
var temp Alias
|
||||||
|
if err := json.Unmarshal(data, &temp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if temp.Name == "" {
|
||||||
|
return errors.New(errors.TypeInvalidInput, ErrCodeServiceAccountFactorAPIkeyInvalidInput, "name cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
*key = UpdatableFactorAPIKey(temp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
253
pkg/types/serviceaccounttypes/service_acccount.go
Normal file
253
pkg/types/serviceaccounttypes/service_acccount.go
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
package serviceaccounttypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCodeServiceAccountInvalidInput = errors.MustNewCode("service_account_invalid_input")
|
||||||
|
ErrCodeServiceAccountAlreadyExists = errors.MustNewCode("service_account_already_exists")
|
||||||
|
ErrCodeServiceAccountNotFound = errors.MustNewCode("service_account_not_found")
|
||||||
|
ErrCodeServiceAccountRoleAlreadyExists = errors.MustNewCode("service_account_role_already_exists")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
StatusActive = valuer.NewString("active")
|
||||||
|
StatusDisabled = valuer.NewString("disabled")
|
||||||
|
ValidStatus = []valuer.String{StatusActive, StatusDisabled}
|
||||||
|
)
|
||||||
|
|
||||||
|
type StorableServiceAccount struct {
|
||||||
|
bun.BaseModel `bun:"table:service_account,alias:service_account"`
|
||||||
|
|
||||||
|
types.Identifiable
|
||||||
|
types.TimeAuditable
|
||||||
|
Name string `bun:"name"`
|
||||||
|
Email string `bun:"email"`
|
||||||
|
Status valuer.String `bun:"status"`
|
||||||
|
OrgID string `bun:"org_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceAccount struct {
|
||||||
|
types.Identifiable
|
||||||
|
types.TimeAuditable
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
Email valuer.Email `json:"email" required:"true"`
|
||||||
|
Roles []string `json:"roles" required:"true" nullable:"false"`
|
||||||
|
Status valuer.String `json:"status" required:"true"`
|
||||||
|
OrgID valuer.UUID `json:"orgID" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostableServiceAccount struct {
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
Email valuer.Email `json:"email" required:"true"`
|
||||||
|
Roles []string `json:"roles" required:"true" nullable:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdatableServiceAccount struct {
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
Email valuer.Email `json:"email" required:"true"`
|
||||||
|
Roles []string `json:"roles" required:"true" nullable:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdatableServiceAccountStatus struct {
|
||||||
|
Status valuer.String `json:"status" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServiceAccount(name string, email valuer.Email, roles []string, status valuer.String, orgID valuer.UUID) *ServiceAccount {
|
||||||
|
return &ServiceAccount{
|
||||||
|
Identifiable: types.Identifiable{
|
||||||
|
ID: valuer.GenerateUUID(),
|
||||||
|
},
|
||||||
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
Name: name,
|
||||||
|
Email: email,
|
||||||
|
Roles: roles,
|
||||||
|
Status: status,
|
||||||
|
OrgID: orgID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServiceAccountFromStorables(storableServiceAccount *StorableServiceAccount, roles []string) *ServiceAccount {
|
||||||
|
return &ServiceAccount{
|
||||||
|
Identifiable: storableServiceAccount.Identifiable,
|
||||||
|
TimeAuditable: storableServiceAccount.TimeAuditable,
|
||||||
|
Name: storableServiceAccount.Name,
|
||||||
|
Email: valuer.MustNewEmail(storableServiceAccount.Email),
|
||||||
|
Roles: roles,
|
||||||
|
Status: storableServiceAccount.Status,
|
||||||
|
OrgID: valuer.MustNewUUID(storableServiceAccount.OrgID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServiceAccountsFromRoles(storableServiceAccounts []*StorableServiceAccount, roles []*roletypes.Role, serviceAccountIDToRoleIDsMap map[string][]valuer.UUID) []*ServiceAccount {
|
||||||
|
serviceAccounts := make([]*ServiceAccount, 0, len(storableServiceAccounts))
|
||||||
|
|
||||||
|
roleIDToRole := make(map[string]*roletypes.Role, len(roles))
|
||||||
|
for _, role := range roles {
|
||||||
|
roleIDToRole[role.ID.String()] = role
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sa := range storableServiceAccounts {
|
||||||
|
roleIDs := serviceAccountIDToRoleIDsMap[sa.ID.String()]
|
||||||
|
|
||||||
|
roleNames := make([]string, len(roleIDs))
|
||||||
|
for idx, rid := range roleIDs {
|
||||||
|
if role, ok := roleIDToRole[rid.String()]; ok {
|
||||||
|
roleNames[idx] = role.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
account := NewServiceAccountFromStorables(sa, roleNames)
|
||||||
|
serviceAccounts = append(serviceAccounts, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceAccounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStorableServiceAccount(serviceAccount *ServiceAccount) *StorableServiceAccount {
|
||||||
|
return &StorableServiceAccount{
|
||||||
|
Identifiable: serviceAccount.Identifiable,
|
||||||
|
TimeAuditable: serviceAccount.TimeAuditable,
|
||||||
|
Name: serviceAccount.Name,
|
||||||
|
Email: serviceAccount.Email.String(),
|
||||||
|
Status: serviceAccount.Status,
|
||||||
|
OrgID: serviceAccount.OrgID.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sa *ServiceAccount) Update(name string, email valuer.Email, roles []string) {
|
||||||
|
sa.Name = name
|
||||||
|
sa.Email = email
|
||||||
|
sa.Roles = roles
|
||||||
|
sa.UpdatedAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sa *ServiceAccount) UpdateStatus(status valuer.String) {
|
||||||
|
sa.Status = status
|
||||||
|
sa.UpdatedAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sa *ServiceAccount) NewFactorAPIKey(name string, expiresAt uint64) (*FactorAPIKey, error) {
|
||||||
|
key := make([]byte, 32)
|
||||||
|
_, err := rand.Read(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(errors.TypeInternal, errors.CodeInternal, "failed to generate token")
|
||||||
|
}
|
||||||
|
// Encode the token in base64.
|
||||||
|
encodedKey := base64.StdEncoding.EncodeToString(key)
|
||||||
|
|
||||||
|
return &FactorAPIKey{
|
||||||
|
Identifiable: types.Identifiable{
|
||||||
|
ID: valuer.GenerateUUID(),
|
||||||
|
},
|
||||||
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
Name: name,
|
||||||
|
Key: encodedKey,
|
||||||
|
ExpiresAt: expiresAt,
|
||||||
|
LastUsed: time.Now(),
|
||||||
|
ServiceAccountID: sa.ID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sa *ServiceAccount) PatchRoles(input *ServiceAccount) ([]string, []string) {
|
||||||
|
currentRolesSet := make(map[string]struct{}, len(sa.Roles))
|
||||||
|
inputRolesSet := make(map[string]struct{}, len(input.Roles))
|
||||||
|
|
||||||
|
for _, role := range sa.Roles {
|
||||||
|
currentRolesSet[role] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, role := range input.Roles {
|
||||||
|
inputRolesSet[role] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// additions: roles present in input but not in current
|
||||||
|
additions := []string{}
|
||||||
|
for _, role := range input.Roles {
|
||||||
|
if _, exists := currentRolesSet[role]; !exists {
|
||||||
|
additions = append(additions, role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deletions: roles present in current but not in input
|
||||||
|
deletions := []string{}
|
||||||
|
for _, role := range sa.Roles {
|
||||||
|
if _, exists := inputRolesSet[role]; !exists {
|
||||||
|
deletions = append(deletions, role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return additions, deletions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sa *PostableServiceAccount) UnmarshalJSON(data []byte) error {
|
||||||
|
type Alias PostableServiceAccount
|
||||||
|
|
||||||
|
var temp Alias
|
||||||
|
if err := json.Unmarshal(data, &temp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if temp.Name == "" {
|
||||||
|
return errors.New(errors.TypeInvalidInput, ErrCodeServiceAccountInvalidInput, "name cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(temp.Roles) == 0 {
|
||||||
|
return errors.New(errors.TypeInvalidInput, ErrCodeServiceAccountInvalidInput, "roles cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
*sa = PostableServiceAccount(temp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sa *UpdatableServiceAccount) UnmarshalJSON(data []byte) error {
|
||||||
|
type Alias UpdatableServiceAccount
|
||||||
|
|
||||||
|
var temp Alias
|
||||||
|
if err := json.Unmarshal(data, &temp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if temp.Name == "" {
|
||||||
|
return errors.New(errors.TypeInvalidInput, ErrCodeServiceAccountInvalidInput, "name cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(temp.Roles) == 0 {
|
||||||
|
return errors.New(errors.TypeInvalidInput, ErrCodeServiceAccountInvalidInput, "roles cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
*sa = UpdatableServiceAccount(temp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sa *UpdatableServiceAccountStatus) UnmarshalJSON(data []byte) error {
|
||||||
|
type Alias UpdatableServiceAccountStatus
|
||||||
|
|
||||||
|
var temp Alias
|
||||||
|
if err := json.Unmarshal(data, &temp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(ValidStatus, temp.Status) {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, ErrCodeServiceAccountInvalidInput, "invalid status: %s, allowed status are: %v", temp.Status, ValidStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
*sa = UpdatableServiceAccountStatus(temp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
81
pkg/types/serviceaccounttypes/service_account_role.go
Normal file
81
pkg/types/serviceaccounttypes/service_account_role.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package serviceaccounttypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StorableServiceAccountRole struct {
|
||||||
|
bun.BaseModel `bun:"table:service_account_role,alias:service_account_role"`
|
||||||
|
|
||||||
|
types.Identifiable
|
||||||
|
types.TimeAuditable
|
||||||
|
ServiceAccountID string `bun:"service_account_id"`
|
||||||
|
RoleID string `bun:"role_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStorableServiceAccountRoles(serviceAccountID valuer.UUID, roles []*roletypes.Role) []*StorableServiceAccountRole {
|
||||||
|
storableServiceAccountRoles := make([]*StorableServiceAccountRole, len(roles))
|
||||||
|
for idx, role := range roles {
|
||||||
|
storableServiceAccountRoles[idx] = &StorableServiceAccountRole{
|
||||||
|
Identifiable: types.Identifiable{
|
||||||
|
ID: valuer.GenerateUUID(),
|
||||||
|
},
|
||||||
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
ServiceAccountID: serviceAccountID.String(),
|
||||||
|
RoleID: role.ID.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return storableServiceAccountRoles
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRolesFromStorableServiceAccountRoles(storable []*StorableServiceAccountRole, roles []*roletypes.Role) ([]string, error) {
|
||||||
|
roleIDToName := make(map[string]string, len(roles))
|
||||||
|
for _, role := range roles {
|
||||||
|
roleIDToName[role.ID.String()] = role.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
names := make([]string, 0, len(storable))
|
||||||
|
for _, sar := range storable {
|
||||||
|
roleName, ok := roleIDToName[sar.RoleID]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Newf(errors.TypeInternal, errors.CodeInternal, "role id %s not found in provided roles", sar.RoleID)
|
||||||
|
}
|
||||||
|
names = append(names, roleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUniqueRolesAndServiceAccountMapping(storableServiceAccountRoles []*StorableServiceAccountRole) (map[string][]valuer.UUID, []valuer.UUID) {
|
||||||
|
serviceAccountIDRoles := make(map[string][]valuer.UUID)
|
||||||
|
uniqueRoleIDSet := make(map[string]struct{})
|
||||||
|
|
||||||
|
for _, sar := range storableServiceAccountRoles {
|
||||||
|
saID := sar.ServiceAccountID
|
||||||
|
roleID := sar.RoleID
|
||||||
|
if _, ok := serviceAccountIDRoles[saID]; !ok {
|
||||||
|
serviceAccountIDRoles[saID] = make([]valuer.UUID, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
roleUUID := valuer.MustNewUUID(roleID)
|
||||||
|
serviceAccountIDRoles[saID] = append(serviceAccountIDRoles[saID], roleUUID)
|
||||||
|
uniqueRoleIDSet[roleID] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
roleIDs := make([]valuer.UUID, 0, len(uniqueRoleIDSet))
|
||||||
|
for rid := range uniqueRoleIDSet {
|
||||||
|
roleIDs = append(roleIDs, valuer.MustNewUUID(rid))
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceAccountIDRoles, roleIDs
|
||||||
|
}
|
||||||
33
pkg/types/serviceaccounttypes/store.go
Normal file
33
pkg/types/serviceaccounttypes/store.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package serviceaccounttypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Store interface {
|
||||||
|
// Service Account
|
||||||
|
Create(context.Context, *StorableServiceAccount) error
|
||||||
|
Get(context.Context, valuer.UUID, valuer.UUID) (*StorableServiceAccount, error)
|
||||||
|
GetByID(context.Context, valuer.UUID) (*StorableServiceAccount, error)
|
||||||
|
List(context.Context, valuer.UUID) ([]*StorableServiceAccount, error)
|
||||||
|
Update(context.Context, valuer.UUID, *StorableServiceAccount) error
|
||||||
|
Delete(context.Context, valuer.UUID, valuer.UUID) error
|
||||||
|
|
||||||
|
// Service Account Role
|
||||||
|
CreateServiceAccountRoles(context.Context, []*StorableServiceAccountRole) error
|
||||||
|
GetServiceAccountRoles(context.Context, valuer.UUID) ([]*StorableServiceAccountRole, error)
|
||||||
|
ListServiceAccountRolesByOrgID(context.Context, valuer.UUID) ([]*StorableServiceAccountRole, error)
|
||||||
|
DeleteServiceAccountRoles(context.Context, valuer.UUID) error
|
||||||
|
|
||||||
|
// Service Account Factor API Key
|
||||||
|
CreateFactorAPIKey(context.Context, *StorableFactorAPIKey) error
|
||||||
|
GetFactorAPIKey(context.Context, valuer.UUID, valuer.UUID) (*StorableFactorAPIKey, error)
|
||||||
|
ListFactorAPIKey(context.Context, valuer.UUID) ([]*StorableFactorAPIKey, error)
|
||||||
|
UpdateFactorAPIKey(context.Context, valuer.UUID, *StorableFactorAPIKey) error
|
||||||
|
RevokeFactorAPIKey(context.Context, valuer.UUID, valuer.UUID) error
|
||||||
|
RevokeAllFactorAPIKeys(context.Context, valuer.UUID) error
|
||||||
|
|
||||||
|
RunInTx(context.Context, func(context.Context) error) error
|
||||||
|
}
|
||||||
88
templates/email/api_key_event.gotmpl
Normal file
88
templates/email/api_key_event.gotmpl
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>{{.subject}}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;line-height:1.6;color:#333;background:#fff">
|
||||||
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#fff">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:0">
|
||||||
|
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width:600px;width:100%">
|
||||||
|
{{ if .format.Header.Enabled }}
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:16px 20px 16px">
|
||||||
|
<img src="{{.format.Header.LogoURL}}" alt="SigNoz" width="160" height="40" style="display:block;border:0;outline:none;max-width:100%;height:auto">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
<tr>
|
||||||
|
<td style="padding:16px 20px 16px">
|
||||||
|
<p style="margin:0 0 16px;font-size:16px;color:#333">
|
||||||
|
Hi there,
|
||||||
|
</p>
|
||||||
|
<p style="margin:0 0 16px;font-size:16px;color:#333;line-height:1.6">
|
||||||
|
An API key was {{.Event}} for your service account <strong>{{.Name}}</strong>.
|
||||||
|
</p>
|
||||||
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin:0 0 16px">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:20px;background:#f5f5f5;border-radius:6px;border-left:4px solid #4E74F8">
|
||||||
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 0 8px">
|
||||||
|
<p style="margin:0;font-size:15px;color:#333;line-height:1.6">
|
||||||
|
<strong>Key ID:</strong> {{.KeyID}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 0 8px">
|
||||||
|
<p style="margin:0;font-size:15px;color:#333;line-height:1.6">
|
||||||
|
<strong>Key Name:</strong> {{.KeyName}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 0 8px">
|
||||||
|
<p style="margin:0;font-size:15px;color:#333;line-height:1.6">
|
||||||
|
<strong>Created At:</strong> {{.KeyCreatedAt}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
{{ if .format.Help.Enabled }}
|
||||||
|
<p style="margin:0 0 16px;font-size:16px;color:#333;line-height:1.6">
|
||||||
|
Need help? Chat with our team in the SigNoz application or email us at <a href="mailto:{{.format.Help.Email}}" style="color:#4E74F8;text-decoration:none">{{.format.Help.Email}}</a>.
|
||||||
|
</p>
|
||||||
|
{{ end }}
|
||||||
|
<p style="margin:0;font-size:16px;color:#333;line-height:1.6">
|
||||||
|
Thanks,<br><strong>The SigNoz Team</strong>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ if .format.Footer.Enabled }}
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:8px 16px 8px">
|
||||||
|
<p style="margin:0 0 8px;font-size:12px;color:#999;line-height:1.5">
|
||||||
|
<a href="https://signoz.io/terms-of-service/" style="color:#4E74F8;text-decoration:none">Terms of Service</a> - <a href="https://signoz.io/privacy/" style="color:#4E74F8;text-decoration:none">Privacy Policy</a>
|
||||||
|
</p>
|
||||||
|
<p style="margin:0;font-size:12px;color:#999;line-height:1.5">
|
||||||
|
© 2026 SigNoz Inc.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user