mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-29 05:12:01 +08:00
Fix: Multitenancy support for ORG (#7155)
* fix: support multitenancy in org * fix: register and login working now * fix: changes to migration * fix: migrations run both on sqlite and postgres * fix: remove user flags from fe and be * fix: remove ingestion keys from update * fix: multitenancy support for apdex settings * fix: render ts for users correctly * fix: fix migration to run for new tenants * fix: clean up migrations * fix: address comments * Update pkg/sqlmigration/013_update_organization.go Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * fix: fix build * fix: force invites with org id * Update pkg/query-service/auth/auth.go Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * fix: address comments * fix: address comments * fix: provier with their own dialect * fix: update dialects * fix: remove unwanted change * Update pkg/query-service/app/http_handler.go Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * fix: different files for types --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
This commit is contained in:
parent
296a444bd8
commit
2d73f91380
@ -18,6 +18,7 @@ import (
|
||||
baseconstants "go.signoz.io/signoz/pkg/query-service/constants"
|
||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -45,7 +46,7 @@ func (ah *APIHandler) CloudIntegrationsGenerateConnectionParams(w http.ResponseW
|
||||
return
|
||||
}
|
||||
|
||||
apiKey, apiErr := ah.getOrCreateCloudIntegrationPAT(r.Context(), currentUser.OrgId, cloudProvider)
|
||||
apiKey, apiErr := ah.getOrCreateCloudIntegrationPAT(r.Context(), currentUser.OrgID, cloudProvider)
|
||||
if apiErr != nil {
|
||||
RespondError(w, basemodel.WrapApiError(
|
||||
apiErr, "couldn't provision PAT for cloud integration:",
|
||||
@ -124,7 +125,7 @@ func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId
|
||||
))
|
||||
}
|
||||
for _, p := range allPats {
|
||||
if p.UserID == integrationUser.Id && p.Name == integrationPATName {
|
||||
if p.UserID == integrationUser.ID && p.Name == integrationPATName {
|
||||
return p.Token, nil
|
||||
}
|
||||
}
|
||||
@ -136,7 +137,7 @@ func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId
|
||||
|
||||
newPAT := model.PAT{
|
||||
Token: generatePATToken(),
|
||||
UserID: integrationUser.Id,
|
||||
UserID: integrationUser.ID,
|
||||
Name: integrationPATName,
|
||||
Role: baseconstants.ViewerGroup,
|
||||
ExpiresAt: 0,
|
||||
@ -154,7 +155,7 @@ func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId
|
||||
|
||||
func (ah *APIHandler) getOrCreateCloudIntegrationUser(
|
||||
ctx context.Context, orgId string, cloudProvider string,
|
||||
) (*basemodel.User, *basemodel.ApiError) {
|
||||
) (*types.User, *basemodel.ApiError) {
|
||||
cloudIntegrationUserId := fmt.Sprintf("%s-integration", cloudProvider)
|
||||
|
||||
integrationUserResult, apiErr := ah.AppDao().GetUser(ctx, cloudIntegrationUserId)
|
||||
@ -171,19 +172,21 @@ func (ah *APIHandler) getOrCreateCloudIntegrationUser(
|
||||
zap.String("cloudProvider", cloudProvider),
|
||||
)
|
||||
|
||||
newUser := &basemodel.User{
|
||||
Id: cloudIntegrationUserId,
|
||||
newUser := &types.User{
|
||||
ID: cloudIntegrationUserId,
|
||||
Name: fmt.Sprintf("%s integration", cloudProvider),
|
||||
Email: fmt.Sprintf("%s@signoz.io", cloudIntegrationUserId),
|
||||
CreatedAt: time.Now().Unix(),
|
||||
OrgId: orgId,
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
OrgID: orgId,
|
||||
}
|
||||
|
||||
viewerGroup, apiErr := dao.DB().GetGroupByName(ctx, baseconstants.ViewerGroup)
|
||||
if apiErr != nil {
|
||||
return nil, basemodel.WrapApiError(apiErr, "couldn't get viewer group for creating integration user")
|
||||
}
|
||||
newUser.GroupId = viewerGroup.ID
|
||||
newUser.GroupID = viewerGroup.ID
|
||||
|
||||
passwordHash, err := auth.PasswordHash(uuid.NewString())
|
||||
if err != nil {
|
||||
|
@ -54,7 +54,7 @@ func (ah *APIHandler) createPAT(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// All the PATs are associated with the user creating the PAT.
|
||||
pat.UserID = user.Id
|
||||
pat.UserID = user.ID
|
||||
pat.CreatedAt = time.Now().Unix()
|
||||
pat.UpdatedAt = time.Now().Unix()
|
||||
pat.LastUsed = 0
|
||||
@ -112,7 +112,7 @@ func (ah *APIHandler) updatePAT(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
req.UpdatedByUserID = user.Id
|
||||
req.UpdatedByUserID = user.ID
|
||||
id := mux.Vars(r)["id"]
|
||||
req.UpdatedAt = time.Now().Unix()
|
||||
zap.L().Info("Got Update PAT request", zap.Any("pat", req))
|
||||
@ -135,7 +135,7 @@ func (ah *APIHandler) getPATs(w http.ResponseWriter, r *http.Request) {
|
||||
}, nil)
|
||||
return
|
||||
}
|
||||
zap.L().Info("Get PATs for user", zap.String("user_id", user.Id))
|
||||
zap.L().Info("Get PATs for user", zap.String("user_id", user.ID))
|
||||
pats, apierr := ah.AppDao().ListPATs(ctx)
|
||||
if apierr != nil {
|
||||
RespondError(w, apierr, nil)
|
||||
@ -157,7 +157,7 @@ func (ah *APIHandler) revokePAT(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
zap.L().Info("Revoke PAT with id", zap.String("id", id))
|
||||
if apierr := ah.AppDao().RevokePAT(ctx, id, user.Id); apierr != nil {
|
||||
if apierr := ah.AppDao().RevokePAT(ctx, id, user.ID); apierr != nil {
|
||||
RespondError(w, apierr, nil)
|
||||
return
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"go.signoz.io/signoz/ee/query-service/rules"
|
||||
"go.signoz.io/signoz/pkg/http/middleware"
|
||||
"go.signoz.io/signoz/pkg/signoz"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
"go.signoz.io/signoz/pkg/web"
|
||||
|
||||
@ -340,14 +341,14 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
|
||||
r := baseapp.NewRouter()
|
||||
|
||||
// add auth middleware
|
||||
getUserFromRequest := func(ctx context.Context) (*basemodel.UserPayload, error) {
|
||||
getUserFromRequest := func(ctx context.Context) (*types.GettableUser, error) {
|
||||
user, err := auth.GetUserFromRequestContext(ctx, apiHandler)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.User.OrgId == "" {
|
||||
if user.User.OrgID == "" {
|
||||
return nil, basemodel.UnauthorizedError(errors.New("orgId is missing in the claims"))
|
||||
}
|
||||
|
||||
|
@ -7,14 +7,14 @@ import (
|
||||
|
||||
"go.signoz.io/signoz/ee/query-service/app/api"
|
||||
baseauth "go.signoz.io/signoz/pkg/query-service/auth"
|
||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func GetUserFromRequestContext(ctx context.Context, apiHandler *api.APIHandler) (*basemodel.UserPayload, error) {
|
||||
func GetUserFromRequestContext(ctx context.Context, apiHandler *api.APIHandler) (*types.GettableUser, error) {
|
||||
patToken, ok := authtypes.UUIDFromContext(ctx)
|
||||
if ok && patToken != "" {
|
||||
zap.L().Debug("Received a non-zero length PAT token")
|
||||
@ -40,9 +40,9 @@ func GetUserFromRequestContext(ctx context.Context, apiHandler *api.APIHandler)
|
||||
}
|
||||
telemetry.GetInstance().SetPatTokenUser()
|
||||
dao.UpdatePATLastUsed(ctx, patToken, time.Now().Unix())
|
||||
user.User.GroupId = group.ID
|
||||
user.User.Id = pat.Id
|
||||
return &basemodel.UserPayload{
|
||||
user.User.GroupID = group.ID
|
||||
user.User.ID = pat.Id
|
||||
return &types.GettableUser{
|
||||
User: user.User,
|
||||
Role: pat.Role,
|
||||
}, nil
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
basedao "go.signoz.io/signoz/pkg/query-service/dao"
|
||||
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
|
||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
)
|
||||
|
||||
@ -39,7 +40,7 @@ type ModelDao interface {
|
||||
GetPAT(ctx context.Context, pat string) (*model.PAT, basemodel.BaseApiError)
|
||||
UpdatePATLastUsed(ctx context.Context, pat string, lastUsed int64) basemodel.BaseApiError
|
||||
GetPATByID(ctx context.Context, id string) (*model.PAT, basemodel.BaseApiError)
|
||||
GetUserByPAT(ctx context.Context, token string) (*basemodel.UserPayload, basemodel.BaseApiError)
|
||||
GetUserByPAT(ctx context.Context, token string) (*types.GettableUser, basemodel.BaseApiError)
|
||||
ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApiError)
|
||||
RevokePAT(ctx context.Context, id string, userID string) basemodel.BaseApiError
|
||||
}
|
||||
|
@ -14,11 +14,12 @@ import (
|
||||
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
|
||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (*basemodel.User, basemodel.BaseApiError) {
|
||||
func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (*types.User, basemodel.BaseApiError) {
|
||||
// get auth domain from email domain
|
||||
domain, apierr := m.GetDomainByEmail(ctx, email)
|
||||
if apierr != nil {
|
||||
@ -42,15 +43,17 @@ func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (
|
||||
return nil, apiErr
|
||||
}
|
||||
|
||||
user := &basemodel.User{
|
||||
Id: uuid.NewString(),
|
||||
user := &types.User{
|
||||
ID: uuid.NewString(),
|
||||
Name: "",
|
||||
Email: email,
|
||||
Password: hash,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
ProfilePictureURL: "", // Currently unused
|
||||
GroupId: group.ID,
|
||||
OrgId: domain.OrgId,
|
||||
GroupID: group.ID,
|
||||
OrgID: domain.OrgId,
|
||||
}
|
||||
|
||||
user, apiErr = m.CreateUser(ctx, user, false)
|
||||
@ -73,7 +76,7 @@ func (m *modelDao) PrepareSsoRedirect(ctx context.Context, redirectUri, email st
|
||||
return "", model.BadRequestStr("invalid user email received from the auth provider")
|
||||
}
|
||||
|
||||
user := &basemodel.User{}
|
||||
user := &types.User{}
|
||||
|
||||
if userPayload == nil {
|
||||
newUser, apiErr := m.createUserForSAMLRequest(ctx, email)
|
||||
@ -95,7 +98,7 @@ func (m *modelDao) PrepareSsoRedirect(ctx context.Context, redirectUri, email st
|
||||
return fmt.Sprintf("%s?jwt=%s&usr=%s&refreshjwt=%s",
|
||||
redirectUri,
|
||||
tokenStore.AccessJwt,
|
||||
user.Id,
|
||||
user.ID,
|
||||
tokenStore.RefreshJwt), nil
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"go.signoz.io/signoz/ee/query-service/model"
|
||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -42,10 +43,10 @@ func (m *modelDao) CreatePAT(ctx context.Context, p model.PAT) (model.PAT, basem
|
||||
}
|
||||
} else {
|
||||
p.CreatedByUser = model.User{
|
||||
Id: createdByUser.Id,
|
||||
Id: createdByUser.ID,
|
||||
Name: createdByUser.Name,
|
||||
Email: createdByUser.Email,
|
||||
CreatedAt: createdByUser.CreatedAt,
|
||||
CreatedAt: createdByUser.CreatedAt.Unix(),
|
||||
ProfilePictureURL: createdByUser.ProfilePictureURL,
|
||||
NotFound: false,
|
||||
}
|
||||
@ -95,10 +96,10 @@ func (m *modelDao) ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApi
|
||||
}
|
||||
} else {
|
||||
pats[i].CreatedByUser = model.User{
|
||||
Id: createdByUser.Id,
|
||||
Id: createdByUser.ID,
|
||||
Name: createdByUser.Name,
|
||||
Email: createdByUser.Email,
|
||||
CreatedAt: createdByUser.CreatedAt,
|
||||
CreatedAt: createdByUser.CreatedAt.Unix(),
|
||||
ProfilePictureURL: createdByUser.ProfilePictureURL,
|
||||
NotFound: false,
|
||||
}
|
||||
@ -111,10 +112,10 @@ func (m *modelDao) ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApi
|
||||
}
|
||||
} else {
|
||||
pats[i].UpdatedByUser = model.User{
|
||||
Id: updatedByUser.Id,
|
||||
Id: updatedByUser.ID,
|
||||
Name: updatedByUser.Name,
|
||||
Email: updatedByUser.Email,
|
||||
CreatedAt: updatedByUser.CreatedAt,
|
||||
CreatedAt: updatedByUser.CreatedAt.Unix(),
|
||||
ProfilePictureURL: updatedByUser.ProfilePictureURL,
|
||||
NotFound: false,
|
||||
}
|
||||
@ -170,8 +171,8 @@ func (m *modelDao) GetPATByID(ctx context.Context, id string) (*model.PAT, basem
|
||||
}
|
||||
|
||||
// deprecated
|
||||
func (m *modelDao) GetUserByPAT(ctx context.Context, token string) (*basemodel.UserPayload, basemodel.BaseApiError) {
|
||||
users := []basemodel.UserPayload{}
|
||||
func (m *modelDao) GetUserByPAT(ctx context.Context, token string) (*types.GettableUser, basemodel.BaseApiError) {
|
||||
users := []types.GettableUser{}
|
||||
|
||||
query := `SELECT
|
||||
u.id,
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
saml2 "github.com/russellhaering/gosaml2"
|
||||
"go.signoz.io/signoz/ee/query-service/sso"
|
||||
"go.signoz.io/signoz/ee/query-service/sso/saml"
|
||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -33,7 +33,7 @@ type OrgDomain struct {
|
||||
SamlConfig *SamlConfig `json:"samlConfig"`
|
||||
GoogleAuthConfig *GoogleOAuthConfig `json:"googleAuthConfig"`
|
||||
|
||||
Org *basemodel.Organization
|
||||
Org *types.Organization
|
||||
}
|
||||
|
||||
func (od *OrgDomain) String() string {
|
||||
|
@ -1,26 +0,0 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/user/setFlags';
|
||||
|
||||
const setFlags = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.patch(`/user/${props.userId}/flags`, {
|
||||
...props.flags,
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: response.data?.status,
|
||||
payload: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default setFlags;
|
@ -1,4 +0,0 @@
|
||||
export default interface ReleaseNoteProps {
|
||||
path?: string;
|
||||
release?: string;
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import { Button, Space } from 'antd';
|
||||
import setFlags from 'api/user/setFlags';
|
||||
import MessageTip from 'components/MessageTip';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { useCallback } from 'react';
|
||||
import { UserFlags } from 'types/api/user/setFlags';
|
||||
|
||||
import ReleaseNoteProps from '../ReleaseNoteProps';
|
||||
|
||||
export default function ReleaseNote0120({
|
||||
release,
|
||||
}: ReleaseNoteProps): JSX.Element | null {
|
||||
const { user, setUserFlags } = useAppContext();
|
||||
|
||||
const handleDontShow = useCallback(async (): Promise<void> => {
|
||||
const flags: UserFlags = { ReleaseNote0120Hide: 'Y' };
|
||||
|
||||
try {
|
||||
setUserFlags(flags);
|
||||
if (!user) {
|
||||
// no user is set, so escape the routine
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await setFlags({ userId: user.id, flags });
|
||||
|
||||
if (response.statusCode !== 200) {
|
||||
console.log('failed to complete do not show status', response.error);
|
||||
}
|
||||
} catch (e) {
|
||||
// here we do not nothing as the cost of error is minor,
|
||||
// the user can switch the do no show option again in the further.
|
||||
console.log('unexpected error: failed to complete do not show status', e);
|
||||
}
|
||||
}, [setUserFlags, user]);
|
||||
|
||||
return (
|
||||
<MessageTip
|
||||
show
|
||||
message={
|
||||
<div>
|
||||
You are using {release} of SigNoz. We have introduced distributed setup in
|
||||
v0.12.0 release. If you use or plan to use clickhouse queries in dashboard
|
||||
or alerts, you might want to read about querying the new distributed tables{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/operate/migration/upgrade-0.12/#querying-distributed-tables"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
here
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
action={
|
||||
<Space>
|
||||
<Button onClick={handleDontShow}>Do not show again</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import ReleaseNoteProps from 'components/ReleaseNote/ReleaseNoteProps';
|
||||
import ReleaseNote0120 from 'components/ReleaseNote/Releases/ReleaseNote0120';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { UserFlags } from 'types/api/user/setFlags';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
|
||||
interface ComponentMapType {
|
||||
match: (
|
||||
path: string | undefined,
|
||||
version: string,
|
||||
userFlags: UserFlags | null,
|
||||
) => boolean;
|
||||
component: ({ path, release }: ReleaseNoteProps) => JSX.Element | null;
|
||||
}
|
||||
|
||||
const allComponentMap: ComponentMapType[] = [
|
||||
{
|
||||
match: (
|
||||
path: string | undefined,
|
||||
version: string,
|
||||
userFlags: UserFlags | null,
|
||||
): boolean => {
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
const allowedPaths: string[] = [
|
||||
ROUTES.LIST_ALL_ALERT,
|
||||
ROUTES.APPLICATION,
|
||||
ROUTES.ALL_DASHBOARD,
|
||||
];
|
||||
|
||||
return (
|
||||
userFlags?.ReleaseNote0120Hide !== 'Y' &&
|
||||
allowedPaths.includes(path) &&
|
||||
version.startsWith('v0.12')
|
||||
);
|
||||
},
|
||||
component: ReleaseNote0120,
|
||||
},
|
||||
];
|
||||
|
||||
// ReleaseNote prints release specific warnings and notes that
|
||||
// user needs to be aware of before using the upgraded version.
|
||||
function ReleaseNote({ path }: ReleaseNoteProps): JSX.Element | null {
|
||||
const { user } = useAppContext();
|
||||
const { currentVersion } = useSelector<AppState, AppReducer>(
|
||||
(state) => state.app,
|
||||
);
|
||||
|
||||
const c = allComponentMap.find((item) =>
|
||||
item.match(path, currentVersion, user.flags),
|
||||
);
|
||||
|
||||
if (!c) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <c.component path={path} release={currentVersion} />;
|
||||
}
|
||||
|
||||
ReleaseNote.defaultProps = {
|
||||
path: '',
|
||||
};
|
||||
|
||||
export default ReleaseNote;
|
@ -1,21 +1,18 @@
|
||||
import { Space } from 'antd';
|
||||
import getAll from 'api/alerts/getAll';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import ReleaseNote from 'components/ReleaseNote';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { isUndefined } from 'lodash-es';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { AlertsEmptyState } from './AlertsEmptyState/AlertsEmptyState';
|
||||
import ListAlert from './ListAlert';
|
||||
|
||||
function ListAlertRules(): JSX.Element {
|
||||
const { t } = useTranslation('common');
|
||||
const location = useLocation();
|
||||
const { data, isError, isLoading, refetch, status } = useQuery('allAlerts', {
|
||||
queryFn: getAll,
|
||||
cacheTime: 0,
|
||||
@ -70,7 +67,6 @@ function ListAlertRules(): JSX.Element {
|
||||
|
||||
return (
|
||||
<Space direction="vertical" size="large" style={{ width: '100%' }}>
|
||||
<ReleaseNote path={location.pathname} />
|
||||
<ListAlert
|
||||
{...{
|
||||
allAlertRules: data.payload,
|
||||
|
@ -281,7 +281,7 @@ function Members(): JSX.Element {
|
||||
const { joinedOn } = record;
|
||||
return (
|
||||
<Typography>
|
||||
{dayjs.unix(Number(joinedOn)).format(DATE_TIME_FORMATS.MONTH_DATE_FULL)}
|
||||
{dayjs(joinedOn).format(DATE_TIME_FORMATS.MONTH_DATE_FULL)}
|
||||
</Typography>
|
||||
);
|
||||
},
|
||||
|
@ -1,14 +1,10 @@
|
||||
import './DashboardsListPage.styles.scss';
|
||||
|
||||
import { Space, Typography } from 'antd';
|
||||
import ReleaseNote from 'components/ReleaseNote';
|
||||
import ListOfAllDashboard from 'container/ListOfDashboard';
|
||||
import { LayoutGrid } from 'lucide-react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
function DashboardsListPage(): JSX.Element {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<Space
|
||||
direction="vertical"
|
||||
@ -16,7 +12,6 @@ function DashboardsListPage(): JSX.Element {
|
||||
style={{ width: '100%' }}
|
||||
className="dashboard-list-page"
|
||||
>
|
||||
<ReleaseNote path={location.pathname} />
|
||||
<div className="dashboard-header">
|
||||
<LayoutGrid size={14} className="icon" />
|
||||
<Typography.Text className="text">Dashboards</Typography.Text>
|
||||
|
@ -1,15 +1,9 @@
|
||||
import { Space } from 'antd';
|
||||
import ReleaseNote from 'components/ReleaseNote';
|
||||
import ServicesApplication from 'container/ServiceApplication';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
function Metrics(): JSX.Element {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<ReleaseNote path={location.pathname} />
|
||||
|
||||
<ServicesApplication />
|
||||
</Space>
|
||||
);
|
||||
|
@ -21,7 +21,6 @@ import { FeatureFlagProps as FeatureFlags } from 'types/api/features/getFeatures
|
||||
import { PayloadProps as LicensesResModel } from 'types/api/licenses/getAll';
|
||||
import { LicenseV3ResModel } from 'types/api/licensesV3/getActive';
|
||||
import { Organization } from 'types/api/user/getOrganization';
|
||||
import { UserFlags } from 'types/api/user/setFlags';
|
||||
import { OrgPreference } from 'types/reducer/app';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
|
||||
@ -158,13 +157,6 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
||||
}
|
||||
}, [orgPreferencesData, isFetchingOrgPreferences]);
|
||||
|
||||
function setUserFlags(userflags: UserFlags): void {
|
||||
setUser((prev) => ({
|
||||
...prev,
|
||||
flags: userflags,
|
||||
}));
|
||||
}
|
||||
|
||||
function updateUser(user: IUser): void {
|
||||
setUser((prev) => ({
|
||||
...prev,
|
||||
@ -252,7 +244,6 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
||||
orgPreferencesFetchError,
|
||||
licensesRefetch,
|
||||
updateUser,
|
||||
setUserFlags,
|
||||
updateOrgPreferences,
|
||||
updateOrg,
|
||||
}),
|
||||
|
@ -3,7 +3,6 @@ import { PayloadProps as LicensesResModel } from 'types/api/licenses/getAll';
|
||||
import { LicenseV3ResModel } from 'types/api/licensesV3/getActive';
|
||||
import { Organization } from 'types/api/user/getOrganization';
|
||||
import { PayloadProps as User } from 'types/api/user/getUser';
|
||||
import { UserFlags } from 'types/api/user/setFlags';
|
||||
import { OrgPreference } from 'types/reducer/app';
|
||||
|
||||
export interface IAppContext {
|
||||
@ -26,7 +25,6 @@ export interface IAppContext {
|
||||
orgPreferencesFetchError: unknown;
|
||||
licensesRefetch: () => void;
|
||||
updateUser: (user: IUser) => void;
|
||||
setUserFlags: (flags: UserFlags) => void;
|
||||
updateOrgPreferences: (orgPreferences: OrgPreference[]) => void;
|
||||
updateOrg(orgId: string, updatedOrgName: string): void;
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ function getUserDefaults(): IUser {
|
||||
name: '',
|
||||
profilePictureURL: '',
|
||||
createdAt: 0,
|
||||
flags: {},
|
||||
organization: '',
|
||||
orgId: '',
|
||||
role: 'VIEWER',
|
||||
|
@ -128,7 +128,6 @@ export function getAppContextMock(
|
||||
name: 'John Doe',
|
||||
profilePictureURL: '',
|
||||
createdAt: 1732544623,
|
||||
flags: {},
|
||||
organization: 'Nightswatch',
|
||||
orgId: 'does-not-matter-id',
|
||||
role: role as ROLES,
|
||||
@ -326,7 +325,6 @@ export function getAppContextMock(
|
||||
orgPreferencesFetchError: null,
|
||||
isLoggedIn: true,
|
||||
updateUser: jest.fn(),
|
||||
setUserFlags: jest.fn(),
|
||||
updateOrg: jest.fn(),
|
||||
updateOrgPreferences: jest.fn(),
|
||||
licensesRefetch: jest.fn(),
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { UserFlags } from 'types/api/user/setFlags';
|
||||
import { User } from 'types/reducer/app';
|
||||
import { ROLES } from 'types/roles';
|
||||
|
||||
@ -16,6 +15,5 @@ export interface PayloadProps {
|
||||
profilePictureURL: string;
|
||||
organization: string;
|
||||
role: ROLES;
|
||||
flags: UserFlags;
|
||||
groupId: string;
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
import { User } from 'types/reducer/app';
|
||||
|
||||
export interface UserFlags {
|
||||
ReleaseNote0120Hide?: string;
|
||||
}
|
||||
|
||||
export type PayloadProps = UserFlags;
|
||||
|
||||
export interface Props {
|
||||
userId: User['userId'];
|
||||
flags: UserFlags;
|
||||
}
|
@ -1,21 +1,27 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
)
|
||||
|
||||
func (aH *APIHandler) setApdexSettings(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||
if !ok {
|
||||
RespondError(w, &model.ApiError{Err: errors.New("unauthorized"), Typ: model.ErrorUnauthorized}, nil)
|
||||
return
|
||||
}
|
||||
req, err := parseSetApdexScoreRequest(r)
|
||||
if aH.HandleError(w, err, http.StatusBadRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := dao.DB().SetApdexSettings(context.Background(), req); err != nil {
|
||||
if err := dao.DB().SetApdexSettings(r.Context(), claims.OrgID, req); err != nil {
|
||||
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
|
||||
return
|
||||
}
|
||||
@ -25,7 +31,12 @@ func (aH *APIHandler) setApdexSettings(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (aH *APIHandler) getApdexSettings(w http.ResponseWriter, r *http.Request) {
|
||||
services := r.URL.Query().Get("services")
|
||||
apdexSet, err := dao.DB().GetApdexSettings(context.Background(), strings.Split(strings.TrimSpace(services), ","))
|
||||
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||
if !ok {
|
||||
RespondError(w, &model.ApiError{Err: errors.New("unauthorized"), Typ: model.ErrorUnauthorized}, nil)
|
||||
return
|
||||
}
|
||||
apdexSet, err := dao.DB().GetApdexSettings(r.Context(), claims.OrgID, strings.Split(strings.TrimSpace(services), ","))
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
|
||||
return
|
||||
|
@ -9,13 +9,14 @@ import (
|
||||
"go.signoz.io/signoz/pkg/query-service/auth"
|
||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
)
|
||||
|
||||
type AuthMiddleware struct {
|
||||
GetUserFromRequest func(r context.Context) (*model.UserPayload, error)
|
||||
GetUserFromRequest func(r context.Context) (*types.GettableUser, error)
|
||||
}
|
||||
|
||||
func NewAuthMiddleware(f func(ctx context.Context) (*model.UserPayload, error)) *AuthMiddleware {
|
||||
func NewAuthMiddleware(f func(ctx context.Context) (*types.GettableUser, error)) *AuthMiddleware {
|
||||
return &AuthMiddleware{
|
||||
GetUserFromRequest: f,
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.signoz.io/signoz/pkg/query-service/app/metricsexplorer"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
@ -19,6 +18,8 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"go.signoz.io/signoz/pkg/query-service/app/metricsexplorer"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
@ -51,6 +52,7 @@ import (
|
||||
"go.signoz.io/signoz/pkg/query-service/contextlinks"
|
||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||
"go.signoz.io/signoz/pkg/query-service/postprocess"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@ -597,8 +599,6 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) {
|
||||
router.HandleFunc("/api/v1/user/{id}", am.SelfAccess(aH.editUser)).Methods(http.MethodPut)
|
||||
router.HandleFunc("/api/v1/user/{id}", am.AdminAccess(aH.deleteUser)).Methods(http.MethodDelete)
|
||||
|
||||
router.HandleFunc("/api/v1/user/{id}/flags", am.SelfAccess(aH.patchUserFlag)).Methods(http.MethodPatch)
|
||||
|
||||
router.HandleFunc("/api/v1/rbac/role/{id}", am.SelfAccess(aH.getRole)).Methods(http.MethodGet)
|
||||
router.HandleFunc("/api/v1/rbac/role/{id}", am.AdminAccess(aH.editRole)).Methods(http.MethodPut)
|
||||
|
||||
@ -2108,8 +2108,13 @@ func (aH *APIHandler) revokeInvite(w http.ResponseWriter, r *http.Request) {
|
||||
// listPendingInvites is used to list the pending invites.
|
||||
func (aH *APIHandler) listPendingInvites(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ctx := context.Background()
|
||||
invites, err := dao.DB().GetInvites(ctx)
|
||||
ctx := r.Context()
|
||||
claims, ok := authtypes.ClaimsFromContext(ctx)
|
||||
if !ok {
|
||||
RespondError(w, &model.ApiError{Err: errors.New("failed to get org id from context"), Typ: model.ErrorInternal}, nil)
|
||||
return
|
||||
}
|
||||
invites, err := dao.DB().GetInvites(ctx, claims.OrgID)
|
||||
if err != nil {
|
||||
RespondError(w, err, nil)
|
||||
return
|
||||
@ -2120,7 +2125,7 @@ func (aH *APIHandler) listPendingInvites(w http.ResponseWriter, r *http.Request)
|
||||
var resp []*model.InvitationResponseObject
|
||||
for _, inv := range invites {
|
||||
|
||||
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgId)
|
||||
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgID)
|
||||
if apiErr != nil {
|
||||
RespondError(w, apiErr, nil)
|
||||
}
|
||||
@ -2128,7 +2133,7 @@ func (aH *APIHandler) listPendingInvites(w http.ResponseWriter, r *http.Request)
|
||||
Name: inv.Name,
|
||||
Email: inv.Email,
|
||||
Token: inv.Token,
|
||||
CreatedAt: inv.CreatedAt,
|
||||
CreatedAt: inv.CreatedAt.Unix(),
|
||||
Role: inv.Role,
|
||||
Organization: org.Name,
|
||||
})
|
||||
@ -2271,13 +2276,15 @@ func (aH *APIHandler) editUser(w http.ResponseWriter, r *http.Request) {
|
||||
old.ProfilePictureURL = update.ProfilePictureURL
|
||||
}
|
||||
|
||||
_, apiErr = dao.DB().EditUser(ctx, &model.User{
|
||||
Id: old.Id,
|
||||
_, apiErr = dao.DB().EditUser(ctx, &types.User{
|
||||
ID: old.ID,
|
||||
Name: old.Name,
|
||||
OrgId: old.OrgId,
|
||||
OrgID: old.OrgID,
|
||||
Email: old.Email,
|
||||
Password: old.Password,
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: old.CreatedAt,
|
||||
},
|
||||
ProfilePictureURL: old.ProfilePictureURL,
|
||||
})
|
||||
if apiErr != nil {
|
||||
@ -2319,7 +2326,7 @@ func (aH *APIHandler) deleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if user.GroupId == adminGroup.ID && len(adminUsers) == 1 {
|
||||
if user.GroupID == adminGroup.ID && len(adminUsers) == 1 {
|
||||
RespondError(w, &model.ApiError{
|
||||
Typ: model.ErrorInternal,
|
||||
Err: errors.New("cannot delete the last admin user")}, nil)
|
||||
@ -2334,37 +2341,6 @@ func (aH *APIHandler) deleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
aH.WriteJSON(w, r, map[string]string{"data": "user deleted successfully"})
|
||||
}
|
||||
|
||||
// addUserFlag patches a user flags with the changes
|
||||
func (aH *APIHandler) patchUserFlag(w http.ResponseWriter, r *http.Request) {
|
||||
// read user id from path var
|
||||
userId := mux.Vars(r)["id"]
|
||||
|
||||
// read input into user flag
|
||||
defer r.Body.Close()
|
||||
b, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
zap.L().Error("failed read user flags from http request for userId ", zap.String("userId", userId), zap.Error(err))
|
||||
RespondError(w, model.BadRequestStr("received user flags in invalid format"), nil)
|
||||
return
|
||||
}
|
||||
flags := make(map[string]string, 0)
|
||||
|
||||
err = json.Unmarshal(b, &flags)
|
||||
if err != nil {
|
||||
zap.L().Error("failed parsing user flags for userId ", zap.String("userId", userId), zap.Error(err))
|
||||
RespondError(w, model.BadRequestStr("received user flags in invalid format"), nil)
|
||||
return
|
||||
}
|
||||
|
||||
newflags, apiError := dao.DB().UpdateUserFlags(r.Context(), userId, flags)
|
||||
if !apiError.IsNil() {
|
||||
RespondError(w, apiError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
aH.Respond(w, newflags)
|
||||
}
|
||||
|
||||
func (aH *APIHandler) getRole(w http.ResponseWriter, r *http.Request) {
|
||||
id := mux.Vars(r)["id"]
|
||||
|
||||
@ -2380,7 +2356,7 @@ func (aH *APIHandler) getRole(w http.ResponseWriter, r *http.Request) {
|
||||
}, nil)
|
||||
return
|
||||
}
|
||||
group, err := dao.DB().GetGroup(context.Background(), user.GroupId)
|
||||
group, err := dao.DB().GetGroup(context.Background(), user.GroupID)
|
||||
if err != nil {
|
||||
RespondError(w, err, "Failed to get group")
|
||||
return
|
||||
@ -2416,7 +2392,7 @@ func (aH *APIHandler) editRole(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Make sure that the request is not demoting the last admin user.
|
||||
if user.GroupId == auth.AuthCacheObj.AdminGroupId {
|
||||
if user.GroupID == auth.AuthCacheObj.AdminGroupId {
|
||||
adminUsers, apiErr := dao.DB().GetUsersByGroup(ctx, auth.AuthCacheObj.AdminGroupId)
|
||||
if apiErr != nil {
|
||||
RespondError(w, apiErr, "Failed to fetch adminUsers")
|
||||
@ -2431,7 +2407,7 @@ func (aH *APIHandler) editRole(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
apiErr = dao.DB().UpdateUserGroup(context.Background(), user.Id, newGroup.ID)
|
||||
apiErr = dao.DB().UpdateUserGroup(context.Background(), user.ID, newGroup.ID)
|
||||
if apiErr != nil {
|
||||
RespondError(w, apiErr, "Failed to add user to group")
|
||||
return
|
||||
@ -2465,7 +2441,7 @@ func (aH *APIHandler) editOrg(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
req.Id = id
|
||||
req.ID = id
|
||||
if apiErr := dao.DB().EditOrg(context.Background(), req); apiErr != nil {
|
||||
RespondError(w, apiErr, "Failed to update org in the DB")
|
||||
return
|
||||
@ -3528,7 +3504,7 @@ func (aH *APIHandler) getUserPreference(
|
||||
user := common.GetUserFromContext(r.Context())
|
||||
|
||||
preference, apiErr := preferences.GetUserPreference(
|
||||
r.Context(), preferenceId, user.User.OrgId, user.User.Id,
|
||||
r.Context(), preferenceId, user.User.OrgID, user.User.ID,
|
||||
)
|
||||
if apiErr != nil {
|
||||
RespondError(w, apiErr, nil)
|
||||
@ -3551,7 +3527,7 @@ func (aH *APIHandler) updateUserPreference(
|
||||
RespondError(w, model.BadRequest(err), nil)
|
||||
return
|
||||
}
|
||||
preference, apiErr := preferences.UpdateUserPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.Id)
|
||||
preference, apiErr := preferences.UpdateUserPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.ID)
|
||||
if apiErr != nil {
|
||||
RespondError(w, apiErr, nil)
|
||||
return
|
||||
@ -3565,7 +3541,7 @@ func (aH *APIHandler) getAllUserPreferences(
|
||||
) {
|
||||
user := common.GetUserFromContext(r.Context())
|
||||
preference, apiErr := preferences.GetAllUserPreferences(
|
||||
r.Context(), user.User.OrgId, user.User.Id,
|
||||
r.Context(), user.User.OrgID, user.User.ID,
|
||||
)
|
||||
if apiErr != nil {
|
||||
RespondError(w, apiErr, nil)
|
||||
@ -3581,7 +3557,7 @@ func (aH *APIHandler) getOrgPreference(
|
||||
preferenceId := mux.Vars(r)["preferenceId"]
|
||||
user := common.GetUserFromContext(r.Context())
|
||||
preference, apiErr := preferences.GetOrgPreference(
|
||||
r.Context(), preferenceId, user.User.OrgId,
|
||||
r.Context(), preferenceId, user.User.OrgID,
|
||||
)
|
||||
if apiErr != nil {
|
||||
RespondError(w, apiErr, nil)
|
||||
@ -3604,7 +3580,7 @@ func (aH *APIHandler) updateOrgPreference(
|
||||
RespondError(w, model.BadRequest(err), nil)
|
||||
return
|
||||
}
|
||||
preference, apiErr := preferences.UpdateOrgPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.OrgId)
|
||||
preference, apiErr := preferences.UpdateOrgPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.OrgID)
|
||||
if apiErr != nil {
|
||||
RespondError(w, apiErr, nil)
|
||||
return
|
||||
@ -3618,7 +3594,7 @@ func (aH *APIHandler) getAllOrgPreferences(
|
||||
) {
|
||||
user := common.GetUserFromContext(r.Context())
|
||||
preference, apiErr := preferences.GetAllOrgPreferences(
|
||||
r.Context(), user.User.OrgId,
|
||||
r.Context(), user.User.OrgID,
|
||||
)
|
||||
if apiErr != nil {
|
||||
RespondError(w, apiErr, nil)
|
||||
|
@ -6,9 +6,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
|
||||
queues2 "go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/queues"
|
||||
"go.signoz.io/signoz/pkg/query-service/app/integrations/thirdPartyApi"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@ -16,6 +13,10 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
|
||||
queues2 "go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/queues"
|
||||
"go.signoz.io/signoz/pkg/query-service/app/integrations/thirdPartyApi"
|
||||
|
||||
"github.com/SigNoz/govaluate"
|
||||
"github.com/gorilla/mux"
|
||||
promModel "github.com/prometheus/common/model"
|
||||
@ -32,6 +33,7 @@ import (
|
||||
"go.signoz.io/signoz/pkg/query-service/postprocess"
|
||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||
querytemplate "go.signoz.io/signoz/pkg/query-service/utils/queryTemplate"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
)
|
||||
|
||||
var allowedFunctions = []string{"count", "ratePerSec", "sum", "avg", "min", "max", "p50", "p90", "p95", "p99"}
|
||||
@ -465,8 +467,8 @@ func parseGetTTL(r *http.Request) (*model.GetTTLParams, error) {
|
||||
return &model.GetTTLParams{Type: typeTTL}, nil
|
||||
}
|
||||
|
||||
func parseUserRequest(r *http.Request) (*model.User, error) {
|
||||
var req model.User
|
||||
func parseUserRequest(r *http.Request) (*types.User, error) {
|
||||
var req types.User
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -519,8 +521,8 @@ func parseInviteUsersRequest(r *http.Request) (*model.BulkInviteRequest, error)
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func parseSetApdexScoreRequest(r *http.Request) (*model.ApdexSettings, error) {
|
||||
var req model.ApdexSettings
|
||||
func parseSetApdexScoreRequest(r *http.Request) (*types.ApdexSettings, error) {
|
||||
var req types.ApdexSettings
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -566,8 +568,8 @@ func parseUserRoleRequest(r *http.Request) (*model.UserRole, error) {
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func parseEditOrgRequest(r *http.Request) (*model.Organization, error) {
|
||||
var req model.Organization
|
||||
func parseEditOrgRequest(r *http.Request) (*types.Organization, error) {
|
||||
var req types.Organization
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
opAmpModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model"
|
||||
"go.signoz.io/signoz/pkg/query-service/app/preferences"
|
||||
"go.signoz.io/signoz/pkg/signoz"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
"go.signoz.io/signoz/pkg/web"
|
||||
|
||||
@ -291,14 +292,14 @@ func (s *Server) createPublicServer(api *APIHandler, web web.Web) (*http.Server,
|
||||
r.Use(middleware.NewLogging(zap.L(), s.serverOptions.Config.APIServer.Logging.ExcludedRoutes).Wrap)
|
||||
|
||||
// add auth middleware
|
||||
getUserFromRequest := func(ctx context.Context) (*model.UserPayload, error) {
|
||||
getUserFromRequest := func(ctx context.Context) (*types.GettableUser, error) {
|
||||
user, err := auth.GetUserFromReqContext(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.User.OrgId == "" {
|
||||
if user.User.OrgID == "" {
|
||||
return nil, model.UnauthorizedError(errors.New("orgId is missing in the claims"))
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||
smtpservice "go.signoz.io/signoz/pkg/query-service/utils/smtpService"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@ -61,6 +62,10 @@ func Invite(ctx context.Context, req *model.InviteRequest) (*model.InviteRespons
|
||||
return nil, errors.New("User already exists with the same email")
|
||||
}
|
||||
|
||||
claims, ok := authtypes.ClaimsFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to extract OrgID from context")
|
||||
}
|
||||
// Check if an invite already exists
|
||||
invite, apiErr := dao.DB().GetInviteFromEmail(ctx, req.Email)
|
||||
if apiErr != nil {
|
||||
@ -75,23 +80,18 @@ func Invite(ctx context.Context, req *model.InviteRequest) (*model.InviteRespons
|
||||
return nil, errors.Wrap(err, "invalid invite request")
|
||||
}
|
||||
|
||||
claims, ok := authtypes.ClaimsFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errors.Wrap(err, "failed to extract admin user id")
|
||||
}
|
||||
|
||||
au, apiErr := dao.DB().GetUser(ctx, claims.UserID)
|
||||
if apiErr != nil {
|
||||
return nil, errors.Wrap(err, "failed to query admin user from the DB")
|
||||
}
|
||||
|
||||
inv := &model.InvitationObject{
|
||||
inv := &types.Invite{
|
||||
Name: req.Name,
|
||||
Email: req.Email,
|
||||
Token: token,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
CreatedAt: time.Now(),
|
||||
Role: req.Role,
|
||||
OrgId: au.OrgId,
|
||||
OrgID: au.OrgID,
|
||||
}
|
||||
|
||||
if err := dao.DB().CreateInviteEntry(ctx, inv); err != nil {
|
||||
@ -157,7 +157,7 @@ func InviteUsers(ctx context.Context, req *model.BulkInviteRequest) (*model.Bulk
|
||||
}
|
||||
|
||||
// Helper function to handle individual invites
|
||||
func inviteUser(ctx context.Context, req *model.InviteRequest, au *model.UserPayload) (*model.InviteResponse, error) {
|
||||
func inviteUser(ctx context.Context, req *model.InviteRequest, au *types.GettableUser) (*model.InviteResponse, error) {
|
||||
token, err := utils.RandomHex(opaqueTokenSize)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate invite token")
|
||||
@ -186,13 +186,13 @@ func inviteUser(ctx context.Context, req *model.InviteRequest, au *model.UserPay
|
||||
return nil, errors.Wrap(err, "invalid invite request")
|
||||
}
|
||||
|
||||
inv := &model.InvitationObject{
|
||||
inv := &types.Invite{
|
||||
Name: req.Name,
|
||||
Email: req.Email,
|
||||
Token: token,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
CreatedAt: time.Now(),
|
||||
Role: req.Role,
|
||||
OrgId: au.OrgId,
|
||||
OrgID: au.OrgID,
|
||||
}
|
||||
|
||||
if err := dao.DB().CreateInviteEntry(ctx, inv); err != nil {
|
||||
@ -211,7 +211,7 @@ func inviteUser(ctx context.Context, req *model.InviteRequest, au *model.UserPay
|
||||
return &model.InviteResponse{Email: inv.Email, InviteToken: inv.Token}, nil
|
||||
}
|
||||
|
||||
func inviteEmail(req *model.InviteRequest, au *model.UserPayload, token string) {
|
||||
func inviteEmail(req *model.InviteRequest, au *types.GettableUser, token string) {
|
||||
smtp := smtpservice.GetInstance()
|
||||
data := InviteEmailData{
|
||||
CustomerName: req.Name,
|
||||
@ -251,7 +251,12 @@ func RevokeInvite(ctx context.Context, email string) error {
|
||||
return ErrorInvalidInviteToken
|
||||
}
|
||||
|
||||
if err := dao.DB().DeleteInvitation(ctx, email); err != nil {
|
||||
claims, ok := authtypes.ClaimsFromContext(ctx)
|
||||
if !ok {
|
||||
return errors.New("failed to org id from context")
|
||||
}
|
||||
|
||||
if err := dao.DB().DeleteInvitation(ctx, claims.OrgID, email); err != nil {
|
||||
return errors.Wrap(err.Err, "failed to write to DB")
|
||||
}
|
||||
return nil
|
||||
@ -272,7 +277,7 @@ func GetInvite(ctx context.Context, token string) (*model.InvitationResponseObje
|
||||
|
||||
// TODO(Ahsan): This is not the best way to add org name in the invite response. We should
|
||||
// either include org name in the invite table or do a join query.
|
||||
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgId)
|
||||
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgID)
|
||||
if apiErr != nil {
|
||||
return nil, errors.Wrap(apiErr.Err, "failed to query the DB")
|
||||
}
|
||||
@ -280,13 +285,13 @@ func GetInvite(ctx context.Context, token string) (*model.InvitationResponseObje
|
||||
Name: inv.Name,
|
||||
Email: inv.Email,
|
||||
Token: inv.Token,
|
||||
CreatedAt: inv.CreatedAt,
|
||||
CreatedAt: inv.CreatedAt.Unix(),
|
||||
Role: inv.Role,
|
||||
Organization: org.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ValidateInvite(ctx context.Context, req *RegisterRequest) (*model.InvitationObject, error) {
|
||||
func ValidateInvite(ctx context.Context, req *RegisterRequest) (*types.Invite, error) {
|
||||
invitation, err := dao.DB().GetInviteFromEmail(ctx, req.Email)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err.Err, "Failed to read from DB")
|
||||
@ -303,14 +308,14 @@ func ValidateInvite(ctx context.Context, req *RegisterRequest) (*model.Invitatio
|
||||
return invitation, nil
|
||||
}
|
||||
|
||||
func CreateResetPasswordToken(ctx context.Context, userId string) (*model.ResetPasswordEntry, error) {
|
||||
func CreateResetPasswordToken(ctx context.Context, userId string) (*types.ResetPasswordRequest, error) {
|
||||
token, err := utils.RandomHex(opaqueTokenSize)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate reset password token")
|
||||
}
|
||||
|
||||
req := &model.ResetPasswordEntry{
|
||||
UserId: userId,
|
||||
req := &types.ResetPasswordRequest{
|
||||
UserID: userId,
|
||||
Token: token,
|
||||
}
|
||||
if apiErr := dao.DB().CreateResetPasswordEntry(ctx, req); err != nil {
|
||||
@ -334,7 +339,7 @@ func ResetPassword(ctx context.Context, req *model.ResetPasswordRequest) error {
|
||||
return errors.Wrap(err, "Failed to generate password hash")
|
||||
}
|
||||
|
||||
if apiErr := dao.DB().UpdateUserPassword(ctx, hash, entry.UserId); apiErr != nil {
|
||||
if apiErr := dao.DB().UpdateUserPassword(ctx, hash, entry.UserID); apiErr != nil {
|
||||
return apiErr.Err
|
||||
}
|
||||
|
||||
@ -360,7 +365,7 @@ func ChangePassword(ctx context.Context, req *model.ChangePasswordRequest) *mode
|
||||
return model.InternalError(errors.New("Failed to generate password hash"))
|
||||
}
|
||||
|
||||
if apiErr := dao.DB().UpdateUserPassword(ctx, hash, user.Id); apiErr != nil {
|
||||
if apiErr := dao.DB().UpdateUserPassword(ctx, hash, user.ID); apiErr != nil {
|
||||
return apiErr
|
||||
}
|
||||
|
||||
@ -369,6 +374,7 @@ func ChangePassword(ctx context.Context, req *model.ChangePasswordRequest) *mode
|
||||
|
||||
type RegisterRequest struct {
|
||||
Name string `json:"name"`
|
||||
OrgID string `json:"orgId"`
|
||||
OrgName string `json:"orgName"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
@ -380,7 +386,7 @@ type RegisterRequest struct {
|
||||
SourceUrl string `json:"sourceUrl"`
|
||||
}
|
||||
|
||||
func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*model.User, *model.ApiError) {
|
||||
func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*types.User, *model.ApiError) {
|
||||
|
||||
if req.Email == "" {
|
||||
return nil, model.BadRequest(model.ErrEmailRequired{})
|
||||
@ -392,8 +398,9 @@ func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*model.User,
|
||||
|
||||
groupName := constants.AdminGroup
|
||||
|
||||
// modify this to use bun
|
||||
org, apierr := dao.DB().CreateOrg(ctx,
|
||||
&model.Organization{Name: req.OrgName, IsAnonymous: req.IsAnonymous, HasOptedUpdates: req.HasOptedUpdates})
|
||||
&types.Organization{Name: req.OrgName, IsAnonymous: req.IsAnonymous, HasOptedUpdates: req.HasOptedUpdates})
|
||||
if apierr != nil {
|
||||
zap.L().Error("CreateOrg failed", zap.Error(apierr.ToError()))
|
||||
return nil, apierr
|
||||
@ -414,22 +421,24 @@ func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*model.User,
|
||||
return nil, model.InternalError(model.ErrSignupFailed{})
|
||||
}
|
||||
|
||||
user := &model.User{
|
||||
Id: uuid.NewString(),
|
||||
user := &types.User{
|
||||
ID: uuid.NewString(),
|
||||
Name: req.Name,
|
||||
Email: req.Email,
|
||||
Password: hash,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
ProfilePictureURL: "", // Currently unused
|
||||
GroupId: group.ID,
|
||||
OrgId: org.Id,
|
||||
GroupID: group.ID,
|
||||
OrgID: org.ID,
|
||||
}
|
||||
|
||||
return dao.DB().CreateUser(ctx, user, true)
|
||||
}
|
||||
|
||||
// RegisterInvitedUser handles registering a invited user
|
||||
func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword bool) (*model.User, *model.ApiError) {
|
||||
func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword bool) (*types.User, *model.ApiError) {
|
||||
|
||||
if req.InviteToken == "" {
|
||||
return nil, model.BadRequest(ErrorAskAdmin)
|
||||
@ -459,7 +468,7 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b
|
||||
return &userPayload.User, nil
|
||||
}
|
||||
|
||||
if invite.OrgId == "" {
|
||||
if invite.OrgID == "" {
|
||||
zap.L().Error("failed to find org in the invite")
|
||||
return nil, model.InternalError(fmt.Errorf("invalid invite, org not found"))
|
||||
}
|
||||
@ -492,15 +501,17 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b
|
||||
}
|
||||
}
|
||||
|
||||
user := &model.User{
|
||||
Id: uuid.NewString(),
|
||||
user := &types.User{
|
||||
ID: uuid.NewString(),
|
||||
Name: req.Name,
|
||||
Email: req.Email,
|
||||
Password: hash,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
TimeAuditable: types.TimeAuditable{
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
ProfilePictureURL: "", // Currently unused
|
||||
GroupId: group.ID,
|
||||
OrgId: invite.OrgId,
|
||||
GroupID: group.ID,
|
||||
OrgID: invite.OrgID,
|
||||
}
|
||||
|
||||
// TODO(Ahsan): Ideally create user and delete invitation should happen in a txn.
|
||||
@ -510,7 +521,7 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b
|
||||
return nil, apiErr
|
||||
}
|
||||
|
||||
apiErr = dao.DB().DeleteInvitation(ctx, user.Email)
|
||||
apiErr = dao.DB().DeleteInvitation(ctx, user.OrgID, user.Email)
|
||||
if apiErr != nil {
|
||||
zap.L().Error("delete invitation failed", zap.Error(apiErr.Err))
|
||||
return nil, apiErr
|
||||
@ -525,7 +536,7 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b
|
||||
// Register registers a new user. For the first register request, it doesn't need an invite token
|
||||
// and also the first registration is an enforced ADMIN registration. Every subsequent request will
|
||||
// need an invite token to go through.
|
||||
func Register(ctx context.Context, req *RegisterRequest) (*model.User, *model.ApiError) {
|
||||
func Register(ctx context.Context, req *RegisterRequest) (*types.User, *model.ApiError) {
|
||||
users, err := dao.DB().GetUsers(ctx)
|
||||
if err != nil {
|
||||
return nil, model.InternalError(fmt.Errorf("failed to get user count"))
|
||||
@ -562,24 +573,24 @@ func Login(ctx context.Context, request *model.LoginRequest, jwt *authtypes.JWT)
|
||||
|
||||
return &model.LoginResponse{
|
||||
UserJwtObject: userjwt,
|
||||
UserId: user.User.Id,
|
||||
UserId: user.User.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func claimsToUserPayload(claims authtypes.Claims) (*model.UserPayload, error) {
|
||||
user := &model.UserPayload{
|
||||
User: model.User{
|
||||
Id: claims.UserID,
|
||||
GroupId: claims.GroupID,
|
||||
func claimsToUserPayload(claims authtypes.Claims) (*types.GettableUser, error) {
|
||||
user := &types.GettableUser{
|
||||
User: types.User{
|
||||
ID: claims.UserID,
|
||||
GroupID: claims.GroupID,
|
||||
Email: claims.Email,
|
||||
OrgId: claims.OrgID,
|
||||
OrgID: claims.OrgID,
|
||||
},
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// authenticateLogin is responsible for querying the DB and validating the credentials.
|
||||
func authenticateLogin(ctx context.Context, req *model.LoginRequest, jwt *authtypes.JWT) (*model.UserPayload, error) {
|
||||
func authenticateLogin(ctx context.Context, req *model.LoginRequest, jwt *authtypes.JWT) (*types.GettableUser, error) {
|
||||
// If refresh token is valid, then simply authorize the login request.
|
||||
if len(req.RefreshToken) > 0 {
|
||||
// parse the refresh token
|
||||
@ -624,17 +635,17 @@ func passwordMatch(hash, password string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func GenerateJWTForUser(user *model.User, jwt *authtypes.JWT) (model.UserJwtObject, error) {
|
||||
func GenerateJWTForUser(user *types.User, jwt *authtypes.JWT) (model.UserJwtObject, error) {
|
||||
j := model.UserJwtObject{}
|
||||
var err error
|
||||
j.AccessJwtExpiry = time.Now().Add(jwt.JwtExpiry).Unix()
|
||||
j.AccessJwt, err = jwt.AccessToken(user.OrgId, user.Id, user.GroupId, user.Email)
|
||||
j.AccessJwt, err = jwt.AccessToken(user.OrgID, user.ID, user.GroupID, user.Email)
|
||||
if err != nil {
|
||||
return j, errors.Errorf("failed to encode jwt: %v", err)
|
||||
}
|
||||
|
||||
j.RefreshJwtExpiry = time.Now().Add(jwt.JwtRefresh).Unix()
|
||||
j.RefreshJwt, err = jwt.RefreshToken(user.OrgId, user.Id, user.GroupId, user.Email)
|
||||
j.RefreshJwt, err = jwt.RefreshToken(user.OrgID, user.ID, user.GroupID, user.Email)
|
||||
if err != nil {
|
||||
return j, errors.Errorf("failed to encode jwt: %v", err)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
)
|
||||
|
||||
@ -48,28 +48,28 @@ func InitAuthCache(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUserFromReqContext(ctx context.Context) (*model.UserPayload, error) {
|
||||
func GetUserFromReqContext(ctx context.Context) (*types.GettableUser, error) {
|
||||
claims, ok := authtypes.ClaimsFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New("no claims found in context")
|
||||
}
|
||||
|
||||
user := &model.UserPayload{
|
||||
User: model.User{
|
||||
Id: claims.UserID,
|
||||
GroupId: claims.GroupID,
|
||||
user := &types.GettableUser{
|
||||
User: types.User{
|
||||
ID: claims.UserID,
|
||||
GroupID: claims.GroupID,
|
||||
Email: claims.Email,
|
||||
OrgId: claims.OrgID,
|
||||
OrgID: claims.OrgID,
|
||||
},
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func IsSelfAccessRequest(user *model.UserPayload, id string) bool { return user.Id == id }
|
||||
func IsSelfAccessRequest(user *types.GettableUser, id string) bool { return user.ID == id }
|
||||
|
||||
func IsViewer(user *model.UserPayload) bool { return user.GroupId == AuthCacheObj.ViewerGroupId }
|
||||
func IsEditor(user *model.UserPayload) bool { return user.GroupId == AuthCacheObj.EditorGroupId }
|
||||
func IsAdmin(user *model.UserPayload) bool { return user.GroupId == AuthCacheObj.AdminGroupId }
|
||||
func IsViewer(user *types.GettableUser) bool { return user.GroupID == AuthCacheObj.ViewerGroupId }
|
||||
func IsEditor(user *types.GettableUser) bool { return user.GroupID == AuthCacheObj.EditorGroupId }
|
||||
func IsAdmin(user *types.GettableUser) bool { return user.GroupID == AuthCacheObj.AdminGroupId }
|
||||
|
||||
func ValidatePassword(password string) error {
|
||||
if len(password) < minimumPasswordLength {
|
||||
|
@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
|
||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
)
|
||||
|
||||
func GetUserFromContext(ctx context.Context) *model.UserPayload {
|
||||
user, ok := ctx.Value(constants.ContextUserKey).(*model.UserPayload)
|
||||
func GetUserFromContext(ctx context.Context) *types.GettableUser {
|
||||
user, ok := ctx.Value(constants.ContextUserKey).(*types.GettableUser)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
@ -13,28 +13,28 @@ type ModelDao interface {
|
||||
}
|
||||
|
||||
type Queries interface {
|
||||
GetInviteFromEmail(ctx context.Context, email string) (*model.InvitationObject, *model.ApiError)
|
||||
GetInviteFromToken(ctx context.Context, token string) (*model.InvitationObject, *model.ApiError)
|
||||
GetInvites(ctx context.Context) ([]model.InvitationObject, *model.ApiError)
|
||||
GetInviteFromEmail(ctx context.Context, email string) (*types.Invite, *model.ApiError)
|
||||
GetInviteFromToken(ctx context.Context, token string) (*types.Invite, *model.ApiError)
|
||||
GetInvites(ctx context.Context, orgID string) ([]types.Invite, *model.ApiError)
|
||||
|
||||
GetUser(ctx context.Context, id string) (*model.UserPayload, *model.ApiError)
|
||||
GetUserByEmail(ctx context.Context, email string) (*model.UserPayload, *model.ApiError)
|
||||
GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError)
|
||||
GetUsersWithOpts(ctx context.Context, limit int) ([]model.UserPayload, *model.ApiError)
|
||||
GetUser(ctx context.Context, id string) (*types.GettableUser, *model.ApiError)
|
||||
GetUserByEmail(ctx context.Context, email string) (*types.GettableUser, *model.ApiError)
|
||||
GetUsers(ctx context.Context) ([]types.GettableUser, *model.ApiError)
|
||||
GetUsersWithOpts(ctx context.Context, limit int) ([]types.GettableUser, *model.ApiError)
|
||||
|
||||
GetGroup(ctx context.Context, id string) (*model.Group, *model.ApiError)
|
||||
GetGroup(ctx context.Context, id string) (*types.Group, *model.ApiError)
|
||||
GetGroupByName(ctx context.Context, name string) (*types.Group, *model.ApiError)
|
||||
GetGroups(ctx context.Context) ([]model.Group, *model.ApiError)
|
||||
GetGroups(ctx context.Context) ([]types.Group, *model.ApiError)
|
||||
|
||||
GetOrgs(ctx context.Context) ([]model.Organization, *model.ApiError)
|
||||
GetOrgByName(ctx context.Context, name string) (*model.Organization, *model.ApiError)
|
||||
GetOrg(ctx context.Context, id string) (*model.Organization, *model.ApiError)
|
||||
GetOrgs(ctx context.Context) ([]types.Organization, *model.ApiError)
|
||||
GetOrgByName(ctx context.Context, name string) (*types.Organization, *model.ApiError)
|
||||
GetOrg(ctx context.Context, id string) (*types.Organization, *model.ApiError)
|
||||
|
||||
GetResetPasswordEntry(ctx context.Context, token string) (*model.ResetPasswordEntry, *model.ApiError)
|
||||
GetUsersByOrg(ctx context.Context, orgId string) ([]model.UserPayload, *model.ApiError)
|
||||
GetUsersByGroup(ctx context.Context, groupId string) ([]model.UserPayload, *model.ApiError)
|
||||
GetResetPasswordEntry(ctx context.Context, token string) (*types.ResetPasswordRequest, *model.ApiError)
|
||||
GetUsersByOrg(ctx context.Context, orgId string) ([]types.GettableUser, *model.ApiError)
|
||||
GetUsersByGroup(ctx context.Context, groupId string) ([]types.GettableUser, *model.ApiError)
|
||||
|
||||
GetApdexSettings(ctx context.Context, services []string) ([]model.ApdexSettings, *model.ApiError)
|
||||
GetApdexSettings(ctx context.Context, orgID string, services []string) ([]types.ApdexSettings, *model.ApiError)
|
||||
|
||||
GetIngestionKeys(ctx context.Context) ([]model.IngestionKey, *model.ApiError)
|
||||
|
||||
@ -42,29 +42,27 @@ type Queries interface {
|
||||
}
|
||||
|
||||
type Mutations interface {
|
||||
CreateInviteEntry(ctx context.Context, req *model.InvitationObject) *model.ApiError
|
||||
DeleteInvitation(ctx context.Context, email string) *model.ApiError
|
||||
CreateInviteEntry(ctx context.Context, req *types.Invite) *model.ApiError
|
||||
DeleteInvitation(ctx context.Context, orgID string, email string) *model.ApiError
|
||||
|
||||
CreateUser(ctx context.Context, user *model.User, isFirstUser bool) (*model.User, *model.ApiError)
|
||||
EditUser(ctx context.Context, update *model.User) (*model.User, *model.ApiError)
|
||||
CreateUser(ctx context.Context, user *types.User, isFirstUser bool) (*types.User, *model.ApiError)
|
||||
EditUser(ctx context.Context, update *types.User) (*types.User, *model.ApiError)
|
||||
DeleteUser(ctx context.Context, id string) *model.ApiError
|
||||
|
||||
UpdateUserFlags(ctx context.Context, userId string, flags map[string]string) (model.UserFlag, *model.ApiError)
|
||||
|
||||
CreateGroup(ctx context.Context, group *types.Group) (*types.Group, *model.ApiError)
|
||||
DeleteGroup(ctx context.Context, id string) *model.ApiError
|
||||
|
||||
CreateOrg(ctx context.Context, org *model.Organization) (*model.Organization, *model.ApiError)
|
||||
EditOrg(ctx context.Context, org *model.Organization) *model.ApiError
|
||||
CreateOrg(ctx context.Context, org *types.Organization) (*types.Organization, *model.ApiError)
|
||||
EditOrg(ctx context.Context, org *types.Organization) *model.ApiError
|
||||
DeleteOrg(ctx context.Context, id string) *model.ApiError
|
||||
|
||||
CreateResetPasswordEntry(ctx context.Context, req *model.ResetPasswordEntry) *model.ApiError
|
||||
CreateResetPasswordEntry(ctx context.Context, req *types.ResetPasswordRequest) *model.ApiError
|
||||
DeleteResetPasswordEntry(ctx context.Context, token string) *model.ApiError
|
||||
|
||||
UpdateUserPassword(ctx context.Context, hash, userId string) *model.ApiError
|
||||
UpdateUserGroup(ctx context.Context, userId, groupId string) *model.ApiError
|
||||
|
||||
SetApdexSettings(ctx context.Context, set *model.ApdexSettings) *model.ApiError
|
||||
SetApdexSettings(ctx context.Context, orgID string, set *types.ApdexSettings) *model.ApiError
|
||||
|
||||
InsertIngestionKey(ctx context.Context, ingestionKey *model.IngestionKey) *model.ApiError
|
||||
}
|
||||
|
@ -3,24 +3,21 @@ package sqlite
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/uptrace/bun"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
)
|
||||
|
||||
const defaultApdexThreshold = 0.5
|
||||
|
||||
func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []string) ([]model.ApdexSettings, *model.ApiError) {
|
||||
var apdexSettings []model.ApdexSettings
|
||||
func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, orgID string, services []string) ([]types.ApdexSettings, *model.ApiError) {
|
||||
var apdexSettings []types.ApdexSettings
|
||||
|
||||
query, args, err := sqlx.In("SELECT * FROM apdex_settings WHERE service_name IN (?)", services)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
query = mds.db.Rebind(query)
|
||||
|
||||
err = mds.db.Select(&apdexSettings, query, args...)
|
||||
err := mds.bundb.NewSelect().
|
||||
Model(&apdexSettings).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("service_name IN (?)", bun.In(services)).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{
|
||||
Err: err,
|
||||
@ -38,7 +35,7 @@ func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []stri
|
||||
}
|
||||
|
||||
if !found {
|
||||
apdexSettings = append(apdexSettings, model.ApdexSettings{
|
||||
apdexSettings = append(apdexSettings, types.ApdexSettings{
|
||||
ServiceName: service,
|
||||
Threshold: defaultApdexThreshold,
|
||||
})
|
||||
@ -48,18 +45,16 @@ func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []stri
|
||||
return apdexSettings, nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, apdexSettings *model.ApdexSettings) *model.ApiError {
|
||||
func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, orgID string, apdexSettings *types.ApdexSettings) *model.ApiError {
|
||||
// Set the org_id from the parameter since it's required for the foreign key constraint
|
||||
apdexSettings.OrgID = orgID
|
||||
|
||||
_, err := mds.db.NamedExec(`
|
||||
INSERT OR REPLACE INTO apdex_settings (
|
||||
service_name,
|
||||
threshold,
|
||||
exclude_status_codes
|
||||
) VALUES (
|
||||
:service_name,
|
||||
:threshold,
|
||||
:exclude_status_codes
|
||||
)`, apdexSettings)
|
||||
_, err := mds.bundb.NewInsert().
|
||||
Model(apdexSettings).
|
||||
On("CONFLICT (org_id, service_name) DO UPDATE").
|
||||
Set("threshold = EXCLUDED.threshold").
|
||||
Set("exclude_status_codes = EXCLUDED.exclude_status_codes").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return &model.ApiError{
|
||||
Err: err,
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
||||
"go.signoz.io/signoz/pkg/sqlstore"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
@ -62,13 +61,13 @@ func (mds *ModelDaoSqlite) initializeOrgPreferences(ctx context.Context) error {
|
||||
return errors.Errorf("Found %d organizations, expected one or none.", len(orgs))
|
||||
}
|
||||
|
||||
var org model.Organization
|
||||
var org types.Organization
|
||||
if len(orgs) == 1 {
|
||||
org = orgs[0]
|
||||
}
|
||||
|
||||
// set telemetry fields from userPreferences
|
||||
telemetry.GetInstance().SetDistinctId(org.Id)
|
||||
telemetry.GetInstance().SetDistinctId(org.ID)
|
||||
|
||||
users, _ := mds.GetUsers(ctx)
|
||||
countUsers := len(users)
|
||||
|
@ -2,7 +2,6 @@ package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -13,33 +12,38 @@ import (
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
)
|
||||
|
||||
func (mds *ModelDaoSqlite) CreateInviteEntry(ctx context.Context,
|
||||
req *model.InvitationObject) *model.ApiError {
|
||||
func (mds *ModelDaoSqlite) CreateInviteEntry(ctx context.Context, req *types.Invite) *model.ApiError {
|
||||
_, err := mds.bundb.NewInsert().
|
||||
Model(req).
|
||||
Exec(ctx)
|
||||
|
||||
_, err := mds.db.ExecContext(ctx,
|
||||
`INSERT INTO invites (email, name, token, role, created_at, org_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?);`,
|
||||
req.Email, req.Name, req.Token, req.Role, req.CreatedAt, req.OrgId)
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) DeleteInvitation(ctx context.Context, email string) *model.ApiError {
|
||||
_, err := mds.db.ExecContext(ctx, `DELETE from invites where email=?;`, email)
|
||||
func (mds *ModelDaoSqlite) DeleteInvitation(ctx context.Context, orgID string, email string) *model.ApiError {
|
||||
_, err := mds.bundb.NewDelete().
|
||||
Model(&types.Invite{}).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("email = ?", email).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Make this work with org id
|
||||
func (mds *ModelDaoSqlite) GetInviteFromEmail(ctx context.Context, email string,
|
||||
) (*model.InvitationObject, *model.ApiError) {
|
||||
) (*types.Invite, *model.ApiError) {
|
||||
|
||||
invites := []model.InvitationObject{}
|
||||
err := mds.db.Select(&invites,
|
||||
`SELECT * FROM invites WHERE email=?;`, email)
|
||||
invites := []types.Invite{}
|
||||
err := mds.bundb.NewSelect().
|
||||
Model(&invites).
|
||||
Where("email = ?", email).
|
||||
Scan(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
@ -57,11 +61,14 @@ func (mds *ModelDaoSqlite) GetInviteFromEmail(ctx context.Context, email string,
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetInviteFromToken(ctx context.Context, token string,
|
||||
) (*model.InvitationObject, *model.ApiError) {
|
||||
) (*types.Invite, *model.ApiError) {
|
||||
// This won't take org id because it's a public facing API
|
||||
|
||||
invites := []model.InvitationObject{}
|
||||
err := mds.db.Select(&invites,
|
||||
`SELECT * FROM invites WHERE token=?;`, token)
|
||||
invites := []types.Invite{}
|
||||
err := mds.bundb.NewSelect().
|
||||
Model(&invites).
|
||||
Where("token = ?", token).
|
||||
Scan(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
@ -76,11 +83,12 @@ func (mds *ModelDaoSqlite) GetInviteFromToken(ctx context.Context, token string,
|
||||
return &invites[0], nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetInvites(ctx context.Context,
|
||||
) ([]model.InvitationObject, *model.ApiError) {
|
||||
|
||||
invites := []model.InvitationObject{}
|
||||
err := mds.db.Select(&invites, "SELECT * FROM invites")
|
||||
func (mds *ModelDaoSqlite) GetInvites(ctx context.Context, orgID string) ([]types.Invite, *model.ApiError) {
|
||||
invites := []types.Invite{}
|
||||
err := mds.bundb.NewSelect().
|
||||
Model(&invites).
|
||||
Where("org_id = ?", orgID).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
@ -88,13 +96,13 @@ func (mds *ModelDaoSqlite) GetInvites(ctx context.Context,
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) CreateOrg(ctx context.Context,
|
||||
org *model.Organization) (*model.Organization, *model.ApiError) {
|
||||
org *types.Organization) (*types.Organization, *model.ApiError) {
|
||||
|
||||
org.Id = uuid.NewString()
|
||||
org.CreatedAt = time.Now().Unix()
|
||||
_, err := mds.db.ExecContext(ctx,
|
||||
`INSERT INTO organizations (id, name, created_at,is_anonymous,has_opted_updates) VALUES (?, ?, ?, ?, ?);`,
|
||||
org.Id, org.Name, org.CreatedAt, org.IsAnonymous, org.HasOptedUpdates)
|
||||
org.ID = uuid.NewString()
|
||||
org.CreatedAt = time.Now()
|
||||
_, err := mds.bundb.NewInsert().
|
||||
Model(org).
|
||||
Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
@ -103,14 +111,18 @@ func (mds *ModelDaoSqlite) CreateOrg(ctx context.Context,
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetOrg(ctx context.Context,
|
||||
id string) (*model.Organization, *model.ApiError) {
|
||||
id string) (*types.Organization, *model.ApiError) {
|
||||
|
||||
orgs := []model.Organization{}
|
||||
err := mds.db.Select(&orgs, `SELECT * FROM organizations WHERE id=?;`, id)
|
||||
orgs := []types.Organization{}
|
||||
|
||||
if err != nil {
|
||||
if err := mds.bundb.NewSelect().
|
||||
Model(&orgs).
|
||||
Where("id = ?", id).
|
||||
Scan(ctx); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
|
||||
// TODO(nitya): remove for multitenancy
|
||||
if len(orgs) > 1 {
|
||||
return nil, &model.ApiError{
|
||||
Typ: model.ErrorInternal,
|
||||
@ -125,11 +137,14 @@ func (mds *ModelDaoSqlite) GetOrg(ctx context.Context,
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetOrgByName(ctx context.Context,
|
||||
name string) (*model.Organization, *model.ApiError) {
|
||||
name string) (*types.Organization, *model.ApiError) {
|
||||
|
||||
orgs := []model.Organization{}
|
||||
orgs := []types.Organization{}
|
||||
|
||||
if err := mds.db.Select(&orgs, `SELECT * FROM organizations WHERE name=?;`, name); err != nil {
|
||||
if err := mds.bundb.NewSelect().
|
||||
Model(&orgs).
|
||||
Where("name = ?", name).
|
||||
Scan(ctx); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
|
||||
@ -145,9 +160,11 @@ func (mds *ModelDaoSqlite) GetOrgByName(ctx context.Context,
|
||||
return &orgs[0], nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]model.Organization, *model.ApiError) {
|
||||
orgs := []model.Organization{}
|
||||
err := mds.db.Select(&orgs, `SELECT * FROM organizations`)
|
||||
func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]types.Organization, *model.ApiError) {
|
||||
var orgs []types.Organization
|
||||
err := mds.bundb.NewSelect().
|
||||
Model(&orgs).
|
||||
Scan(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
@ -155,24 +172,31 @@ func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]model.Organization, *
|
||||
return orgs, nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) EditOrg(ctx context.Context, org *model.Organization) *model.ApiError {
|
||||
func (mds *ModelDaoSqlite) EditOrg(ctx context.Context, org *types.Organization) *model.ApiError {
|
||||
_, err := mds.bundb.NewUpdate().
|
||||
Model(org).
|
||||
Column("name").
|
||||
Column("has_opted_updates").
|
||||
Column("is_anonymous").
|
||||
Where("id = ?", org.ID).
|
||||
Exec(ctx)
|
||||
|
||||
q := `UPDATE organizations SET name=?,has_opted_updates=?,is_anonymous=? WHERE id=?;`
|
||||
|
||||
_, err := mds.db.ExecContext(ctx, q, org.Name, org.HasOptedUpdates, org.IsAnonymous, org.Id)
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
|
||||
telemetry.GetInstance().SetTelemetryAnonymous(org.IsAnonymous)
|
||||
telemetry.GetInstance().SetDistinctId(org.Id)
|
||||
telemetry.GetInstance().SetDistinctId(org.ID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) DeleteOrg(ctx context.Context, id string) *model.ApiError {
|
||||
_, err := mds.bundb.NewDelete().
|
||||
Model(&types.Organization{}).
|
||||
Where("id = ?", id).
|
||||
Exec(ctx)
|
||||
|
||||
_, err := mds.db.ExecContext(ctx, `DELETE from organizations where id=?;`, id)
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
@ -180,14 +204,10 @@ func (mds *ModelDaoSqlite) DeleteOrg(ctx context.Context, id string) *model.ApiE
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) CreateUser(ctx context.Context,
|
||||
user *model.User, isFirstUser bool) (*model.User, *model.ApiError) {
|
||||
|
||||
_, err := mds.db.ExecContext(ctx,
|
||||
`INSERT INTO users (id, name, email, password, created_at, profile_picture_url, group_id, org_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?,?);`,
|
||||
user.Id, user.Name, user.Email, user.Password, user.CreatedAt,
|
||||
user.ProfilePictureURL, user.GroupId, user.OrgId,
|
||||
)
|
||||
user *types.User, isFirstUser bool) (*types.User, *model.ApiError) {
|
||||
_, err := mds.bundb.NewInsert().
|
||||
Model(user).
|
||||
Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
@ -210,11 +230,15 @@ func (mds *ModelDaoSqlite) CreateUser(ctx context.Context,
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) EditUser(ctx context.Context,
|
||||
update *model.User) (*model.User, *model.ApiError) {
|
||||
update *types.User) (*types.User, *model.ApiError) {
|
||||
_, err := mds.bundb.NewUpdate().
|
||||
Model(update).
|
||||
Column("name").
|
||||
Column("org_id").
|
||||
Column("email").
|
||||
Where("id = ?", update.ID).
|
||||
Exec(ctx)
|
||||
|
||||
_, err := mds.db.ExecContext(ctx,
|
||||
`UPDATE users SET name=?,org_id=?,email=? WHERE id=?;`, update.Name,
|
||||
update.OrgId, update.Email, update.Id)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
@ -224,8 +248,13 @@ func (mds *ModelDaoSqlite) EditUser(ctx context.Context,
|
||||
func (mds *ModelDaoSqlite) UpdateUserPassword(ctx context.Context, passwordHash,
|
||||
userId string) *model.ApiError {
|
||||
|
||||
q := `UPDATE users SET password=? WHERE id=?;`
|
||||
if _, err := mds.db.ExecContext(ctx, q, passwordHash, userId); err != nil {
|
||||
_, err := mds.bundb.NewUpdate().
|
||||
Model(&types.User{}).
|
||||
Set("password = ?", passwordHash).
|
||||
Where("id = ?", userId).
|
||||
Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
return nil
|
||||
@ -233,16 +262,24 @@ func (mds *ModelDaoSqlite) UpdateUserPassword(ctx context.Context, passwordHash,
|
||||
|
||||
func (mds *ModelDaoSqlite) UpdateUserGroup(ctx context.Context, userId, groupId string) *model.ApiError {
|
||||
|
||||
q := `UPDATE users SET group_id=? WHERE id=?;`
|
||||
if _, err := mds.db.ExecContext(ctx, q, groupId, userId); err != nil {
|
||||
_, err := mds.bundb.NewUpdate().
|
||||
Model(&types.User{}).
|
||||
Set("group_id = ?", groupId).
|
||||
Where("id = ?", userId).
|
||||
Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) DeleteUser(ctx context.Context, id string) *model.ApiError {
|
||||
result, err := mds.bundb.NewDelete().
|
||||
Model(&types.User{}).
|
||||
Where("id = ?", id).
|
||||
Exec(ctx)
|
||||
|
||||
result, err := mds.db.ExecContext(ctx, `DELETE from users where id=?;`, id)
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
@ -262,30 +299,19 @@ func (mds *ModelDaoSqlite) DeleteUser(ctx context.Context, id string) *model.Api
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetUser(ctx context.Context,
|
||||
id string) (*model.UserPayload, *model.ApiError) {
|
||||
id string) (*types.GettableUser, *model.ApiError) {
|
||||
|
||||
users := []model.UserPayload{}
|
||||
query := `select
|
||||
u.id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.password,
|
||||
u.created_at,
|
||||
u.profile_picture_url,
|
||||
u.org_id,
|
||||
u.group_id,
|
||||
g.name as role,
|
||||
o.name as organization,
|
||||
COALESCE((select uf.flags
|
||||
from user_flags uf
|
||||
where u.id = uf.user_id), '') as flags
|
||||
from users u, groups g, organizations o
|
||||
where
|
||||
g.id=u.group_id and
|
||||
o.id = u.org_id and
|
||||
u.id=?;`
|
||||
users := []types.GettableUser{}
|
||||
query := mds.bundb.NewSelect().
|
||||
Table("users").
|
||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||
ColumnExpr("g.name as role").
|
||||
ColumnExpr("o.name as organization").
|
||||
Join("JOIN groups g ON g.id = users.group_id").
|
||||
Join("JOIN organizations o ON o.id = users.org_id").
|
||||
Where("users.id = ?", id)
|
||||
|
||||
if err := mds.db.Select(&users, query, id); err != nil {
|
||||
if err := query.Scan(ctx, &users); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
if len(users) > 1 {
|
||||
@ -303,7 +329,7 @@ func (mds *ModelDaoSqlite) GetUser(ctx context.Context,
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
||||
email string) (*model.UserPayload, *model.ApiError) {
|
||||
email string) (*types.GettableUser, *model.ApiError) {
|
||||
|
||||
if email == "" {
|
||||
return nil, &model.ApiError{
|
||||
@ -312,27 +338,20 @@ func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
users := []model.UserPayload{}
|
||||
query := `select
|
||||
u.id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.password,
|
||||
u.created_at,
|
||||
u.profile_picture_url,
|
||||
u.org_id,
|
||||
u.group_id,
|
||||
g.name as role,
|
||||
o.name as organization
|
||||
from users u, groups g, organizations o
|
||||
where
|
||||
g.id=u.group_id and
|
||||
o.id = u.org_id and
|
||||
u.email=?;`
|
||||
users := []types.GettableUser{}
|
||||
query := mds.bundb.NewSelect().
|
||||
Table("users").
|
||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||
ColumnExpr("g.name as role").
|
||||
ColumnExpr("o.name as organization").
|
||||
Join("JOIN groups g ON g.id = users.group_id").
|
||||
Join("JOIN organizations o ON o.id = users.org_id").
|
||||
Where("users.email = ?", email)
|
||||
|
||||
if err := mds.db.Select(&users, query, email); err != nil {
|
||||
if err := query.Scan(ctx, &users); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
|
||||
if len(users) > 1 {
|
||||
return nil, &model.ApiError{
|
||||
Typ: model.ErrorInternal,
|
||||
@ -347,34 +366,26 @@ func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
||||
}
|
||||
|
||||
// GetUsers fetches total user count
|
||||
func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError) {
|
||||
func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]types.GettableUser, *model.ApiError) {
|
||||
return mds.GetUsersWithOpts(ctx, 0)
|
||||
}
|
||||
|
||||
// GetUsersWithOpts fetches users and supports additional search options
|
||||
func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]model.UserPayload, *model.ApiError) {
|
||||
users := []model.UserPayload{}
|
||||
func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]types.GettableUser, *model.ApiError) {
|
||||
users := []types.GettableUser{}
|
||||
|
||||
query := `select
|
||||
u.id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.password,
|
||||
u.created_at,
|
||||
u.profile_picture_url,
|
||||
u.org_id,
|
||||
u.group_id,
|
||||
g.name as role,
|
||||
o.name as organization
|
||||
from users u, groups g, organizations o
|
||||
where
|
||||
g.id = u.group_id and
|
||||
o.id = u.org_id`
|
||||
query := mds.bundb.NewSelect().
|
||||
Table("users").
|
||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||
ColumnExpr("g.name as role").
|
||||
ColumnExpr("o.name as organization").
|
||||
Join("JOIN groups g ON g.id = users.group_id").
|
||||
Join("JOIN organizations o ON o.id = users.org_id")
|
||||
|
||||
if limit > 0 {
|
||||
query = fmt.Sprintf("%s LIMIT %d", query, limit)
|
||||
query.Limit(limit)
|
||||
}
|
||||
err := mds.db.Select(&users, query)
|
||||
err := query.Scan(ctx, &users)
|
||||
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
@ -383,54 +394,42 @@ func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]m
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetUsersByOrg(ctx context.Context,
|
||||
orgId string) ([]model.UserPayload, *model.ApiError) {
|
||||
orgId string) ([]types.GettableUser, *model.ApiError) {
|
||||
|
||||
users := []model.UserPayload{}
|
||||
query := `select
|
||||
u.id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.password,
|
||||
u.created_at,
|
||||
u.profile_picture_url,
|
||||
u.org_id,
|
||||
u.group_id,
|
||||
g.name as role,
|
||||
o.name as organization
|
||||
from users u, groups g, organizations o
|
||||
where
|
||||
u.group_id = g.id and
|
||||
u.org_id = o.id and
|
||||
u.org_id=?;`
|
||||
users := []types.GettableUser{}
|
||||
|
||||
if err := mds.db.Select(&users, query, orgId); err != nil {
|
||||
query := mds.bundb.NewSelect().
|
||||
Table("users").
|
||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||
ColumnExpr("g.name as role").
|
||||
ColumnExpr("o.name as organization").
|
||||
Join("JOIN groups g ON g.id = users.group_id").
|
||||
Join("JOIN organizations o ON o.id = users.org_id").
|
||||
Where("users.org_id = ?", orgId)
|
||||
|
||||
err := query.Scan(ctx, &users)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetUsersByGroup(ctx context.Context,
|
||||
groupId string) ([]model.UserPayload, *model.ApiError) {
|
||||
groupId string) ([]types.GettableUser, *model.ApiError) {
|
||||
|
||||
users := []model.UserPayload{}
|
||||
query := `select
|
||||
u.id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.password,
|
||||
u.created_at,
|
||||
u.profile_picture_url,
|
||||
u.org_id,
|
||||
u.group_id,
|
||||
g.name as role,
|
||||
o.name as organization
|
||||
from users u, groups g, organizations o
|
||||
where
|
||||
u.group_id = g.id and
|
||||
o.id = u.org_id and
|
||||
u.group_id=?;`
|
||||
users := []types.GettableUser{}
|
||||
|
||||
if err := mds.db.Select(&users, query, groupId); err != nil {
|
||||
query := mds.bundb.NewSelect().
|
||||
Table("users").
|
||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||
ColumnExpr("g.name as role").
|
||||
ColumnExpr("o.name as organization").
|
||||
Join("JOIN groups g ON g.id = users.group_id").
|
||||
Join("JOIN organizations o ON o.id = users.org_id").
|
||||
Where("users.group_id = ?", groupId)
|
||||
|
||||
err := query.Scan(ctx, &users)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
return users, nil
|
||||
@ -452,17 +451,25 @@ func (mds *ModelDaoSqlite) CreateGroup(ctx context.Context,
|
||||
|
||||
func (mds *ModelDaoSqlite) DeleteGroup(ctx context.Context, id string) *model.ApiError {
|
||||
|
||||
if _, err := mds.db.ExecContext(ctx, `DELETE from groups where id=?;`, id); err != nil {
|
||||
_, err := mds.bundb.NewDelete().
|
||||
Model(&types.Group{}).
|
||||
Where("id = ?", id).
|
||||
Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetGroup(ctx context.Context,
|
||||
id string) (*model.Group, *model.ApiError) {
|
||||
id string) (*types.Group, *model.ApiError) {
|
||||
|
||||
groups := []model.Group{}
|
||||
if err := mds.db.Select(&groups, `SELECT id, name FROM groups WHERE id=?`, id); err != nil {
|
||||
groups := []types.Group{}
|
||||
if err := mds.bundb.NewSelect().
|
||||
Model(&groups).
|
||||
Where("id = ?", id).
|
||||
Scan(ctx); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
|
||||
@ -505,10 +512,13 @@ func (mds *ModelDaoSqlite) GetGroupByName(ctx context.Context,
|
||||
return &groups[0], nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetGroups(ctx context.Context) ([]model.Group, *model.ApiError) {
|
||||
// TODO(nitya): should have org id
|
||||
func (mds *ModelDaoSqlite) GetGroups(ctx context.Context) ([]types.Group, *model.ApiError) {
|
||||
|
||||
groups := []model.Group{}
|
||||
if err := mds.db.Select(&groups, "SELECT * FROM groups"); err != nil {
|
||||
groups := []types.Group{}
|
||||
if err := mds.bundb.NewSelect().
|
||||
Model(&groups).
|
||||
Scan(ctx); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
|
||||
@ -516,10 +526,11 @@ func (mds *ModelDaoSqlite) GetGroups(ctx context.Context) ([]model.Group, *model
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) CreateResetPasswordEntry(ctx context.Context,
|
||||
req *model.ResetPasswordEntry) *model.ApiError {
|
||||
req *types.ResetPasswordRequest) *model.ApiError {
|
||||
|
||||
q := `INSERT INTO reset_password_request (user_id, token) VALUES (?, ?);`
|
||||
if _, err := mds.db.ExecContext(ctx, q, req.UserId, req.Token); err != nil {
|
||||
if _, err := mds.bundb.NewInsert().
|
||||
Model(req).
|
||||
Exec(ctx); err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
return nil
|
||||
@ -527,7 +538,11 @@ func (mds *ModelDaoSqlite) CreateResetPasswordEntry(ctx context.Context,
|
||||
|
||||
func (mds *ModelDaoSqlite) DeleteResetPasswordEntry(ctx context.Context,
|
||||
token string) *model.ApiError {
|
||||
_, err := mds.db.ExecContext(ctx, `DELETE from reset_password_request where token=?;`, token)
|
||||
_, err := mds.bundb.NewDelete().
|
||||
Model(&types.ResetPasswordRequest{}).
|
||||
Where("token = ?", token).
|
||||
Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
@ -535,12 +550,14 @@ func (mds *ModelDaoSqlite) DeleteResetPasswordEntry(ctx context.Context,
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) GetResetPasswordEntry(ctx context.Context,
|
||||
token string) (*model.ResetPasswordEntry, *model.ApiError) {
|
||||
token string) (*types.ResetPasswordRequest, *model.ApiError) {
|
||||
|
||||
entries := []model.ResetPasswordEntry{}
|
||||
entries := []types.ResetPasswordRequest{}
|
||||
|
||||
q := `SELECT user_id,token FROM reset_password_request WHERE token=?;`
|
||||
if err := mds.db.Select(&entries, q, token); err != nil {
|
||||
if err := mds.bundb.NewSelect().
|
||||
Model(&entries).
|
||||
Where("token = ?", token).
|
||||
Scan(ctx); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
if len(entries) > 1 {
|
||||
@ -554,54 +571,6 @@ func (mds *ModelDaoSqlite) GetResetPasswordEntry(ctx context.Context,
|
||||
return &entries[0], nil
|
||||
}
|
||||
|
||||
// CreateUserFlags inserts user specific flags
|
||||
func (mds *ModelDaoSqlite) UpdateUserFlags(ctx context.Context, userId string, flags map[string]string) (model.UserFlag, *model.ApiError) {
|
||||
|
||||
if len(flags) == 0 {
|
||||
// nothing to do as flags are empty. In this method, we only append the flags
|
||||
// but not set them to empty
|
||||
return flags, nil
|
||||
}
|
||||
|
||||
// fetch existing flags
|
||||
userPayload, apiError := mds.GetUser(ctx, userId)
|
||||
if apiError != nil {
|
||||
return nil, apiError
|
||||
}
|
||||
|
||||
for k, v := range userPayload.Flags {
|
||||
if _, ok := flags[k]; !ok {
|
||||
// insert only missing keys as we want to retain the
|
||||
// flags in the db that are not part of this request
|
||||
flags[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// append existing flags with new ones
|
||||
|
||||
// write the updated flags
|
||||
flagsBytes, err := json.Marshal(flags)
|
||||
if err != nil {
|
||||
return nil, model.InternalError(err)
|
||||
}
|
||||
|
||||
if len(userPayload.Flags) == 0 {
|
||||
q := `INSERT INTO user_flags (user_id, flags) VALUES (?, ?);`
|
||||
|
||||
if _, err := mds.db.ExecContext(ctx, q, userId, string(flagsBytes)); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
} else {
|
||||
q := `UPDATE user_flags SET flags = ? WHERE user_id = ?;`
|
||||
|
||||
if _, err := mds.db.ExecContext(ctx, q, userId, string(flagsBytes)); err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||
}
|
||||
}
|
||||
|
||||
return flags, nil
|
||||
}
|
||||
|
||||
func (mds *ModelDaoSqlite) PrecheckLogin(ctx context.Context, email, sourceUrl string) (*model.PrecheckResponse, model.BaseApiError) {
|
||||
// assume user is valid unless proven otherwise and assign default values for rest of the fields
|
||||
resp := &model.PrecheckResponse{IsUser: true, CanSelfRegister: false, SSO: false, SsoUrl: "", SsoError: ""}
|
||||
|
@ -88,11 +88,6 @@ type ChangePasswordRequest struct {
|
||||
NewPassword string `json:"newPassword"`
|
||||
}
|
||||
|
||||
type ResetPasswordEntry struct {
|
||||
UserId string `json:"userId" db:"user_id"`
|
||||
Token string `json:"token" db:"token"`
|
||||
}
|
||||
|
||||
type UserRole struct {
|
||||
UserId string `json:"user_id"`
|
||||
GroupName string `json:"group_name"`
|
||||
|
@ -1,46 +1,10 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
import "time"
|
||||
|
||||
type Organization struct {
|
||||
Id string `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
CreatedAt int64 `json:"createdAt" db:"created_at"`
|
||||
IsAnonymous bool `json:"isAnonymous" db:"is_anonymous"`
|
||||
HasOptedUpdates bool `json:"hasOptedUpdates" db:"has_opted_updates"`
|
||||
}
|
||||
|
||||
// InvitationObject represents the token object stored in the db
|
||||
type InvitationObject struct {
|
||||
Id string `json:"id" db:"id"`
|
||||
Email string `json:"email" db:"email"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Token string `json:"token" db:"token"`
|
||||
CreatedAt int64 `json:"createdAt" db:"created_at"`
|
||||
Role string `json:"role" db:"role"`
|
||||
OrgId string `json:"orgId" db:"org_id"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Id string `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Email string `json:"email" db:"email"`
|
||||
Password string `json:"password,omitempty" db:"password"`
|
||||
CreatedAt int64 `json:"createdAt" db:"created_at"`
|
||||
ProfilePictureURL string `json:"profilePictureURL" db:"profile_picture_url"`
|
||||
OrgId string `json:"orgId,omitempty" db:"org_id"`
|
||||
GroupId string `json:"groupId,omitempty" db:"group_id"`
|
||||
}
|
||||
|
||||
type ApdexSettings struct {
|
||||
ServiceName string `json:"serviceName" db:"service_name"`
|
||||
Threshold float64 `json:"threshold" db:"threshold"`
|
||||
ExcludeStatusCodes string `json:"excludeStatusCodes" db:"exclude_status_codes"` // sqlite doesn't support array type
|
||||
type ResetPasswordRequest struct {
|
||||
Password string `json:"password"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type IngestionKey struct {
|
||||
@ -51,50 +15,3 @@ type IngestionKey struct {
|
||||
IngestionURL string `json:"ingestionURL" db:"ingestion_url"`
|
||||
DataRegion string `json:"dataRegion" db:"data_region"`
|
||||
}
|
||||
|
||||
type UserFlag map[string]string
|
||||
|
||||
func (uf UserFlag) Value() (driver.Value, error) {
|
||||
f := make(map[string]string, 0)
|
||||
for k, v := range uf {
|
||||
f[k] = v
|
||||
}
|
||||
return json.Marshal(f)
|
||||
}
|
||||
|
||||
func (uf *UserFlag) Scan(value interface{}) error {
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, ok := value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("type assertion to []byte failed while scanning user flag")
|
||||
}
|
||||
f := make(map[string]string, 0)
|
||||
if err := json.Unmarshal([]byte(b), &f); err != nil {
|
||||
return err
|
||||
}
|
||||
*uf = make(UserFlag, len(f))
|
||||
for k, v := range f {
|
||||
(*uf)[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UserPayload struct {
|
||||
User
|
||||
Role string `json:"role"`
|
||||
Organization string `json:"organization"`
|
||||
Flags UserFlag `json:"flags"`
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
Id string `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
}
|
||||
|
||||
type ResetPasswordRequest struct {
|
||||
Password string `json:"password"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||
"go.signoz.io/signoz/pkg/query-service/version"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -206,7 +207,7 @@ type Telemetry struct {
|
||||
alertsInfoCallback func(ctx context.Context) (*model.AlertsInfo, error)
|
||||
userCountCallback func(ctx context.Context) (int, error)
|
||||
userRoleCallback func(ctx context.Context, groupId string) (string, error)
|
||||
getUsersCallback func(ctx context.Context) ([]model.UserPayload, *model.ApiError)
|
||||
getUsersCallback func(ctx context.Context) ([]types.GettableUser, *model.ApiError)
|
||||
dashboardsInfoCallback func(ctx context.Context) (*model.DashboardsInfo, error)
|
||||
savedViewsInfoCallback func(ctx context.Context) (*model.SavedViewsInfo, error)
|
||||
}
|
||||
@ -223,7 +224,7 @@ func (a *Telemetry) SetUserRoleCallback(callback func(ctx context.Context, group
|
||||
a.userRoleCallback = callback
|
||||
}
|
||||
|
||||
func (a *Telemetry) SetGetUsersCallback(callback func(ctx context.Context) ([]model.UserPayload, *model.ApiError)) {
|
||||
func (a *Telemetry) SetGetUsersCallback(callback func(ctx context.Context) ([]types.GettableUser, *model.ApiError)) {
|
||||
a.getUsersCallback = callback
|
||||
}
|
||||
|
||||
@ -524,7 +525,7 @@ func getOutboundIP() string {
|
||||
return string(ip)
|
||||
}
|
||||
|
||||
func (a *Telemetry) IdentifyUser(user *model.User) {
|
||||
func (a *Telemetry) IdentifyUser(user *types.User) {
|
||||
if user.Email == DEFAULT_CLOUD_EMAIL {
|
||||
return
|
||||
}
|
||||
@ -534,7 +535,7 @@ func (a *Telemetry) IdentifyUser(user *model.User) {
|
||||
return
|
||||
}
|
||||
// extract user group from user.groupId
|
||||
role, _ := a.userRoleCallback(context.Background(), user.GroupId)
|
||||
role, _ := a.userRoleCallback(context.Background(), user.GroupID)
|
||||
|
||||
if a.saasOperator != nil {
|
||||
if role != "" {
|
||||
|
@ -17,9 +17,9 @@ import (
|
||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||
"go.signoz.io/signoz/pkg/query-service/featureManager"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -260,7 +260,7 @@ func (tb *FilterSuggestionsTestBed) mockAttribValuesQueryResponse(
|
||||
|
||||
type FilterSuggestionsTestBed struct {
|
||||
t *testing.T
|
||||
testUser *model.User
|
||||
testUser *types.User
|
||||
qsHttpHandler http.Handler
|
||||
mockClickhouse mockhouse.ClickConnMockCommon
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ import (
|
||||
opampModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model"
|
||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||
"go.signoz.io/signoz/pkg/query-service/queryBuilderToExpr"
|
||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||
"go.signoz.io/signoz/pkg/sqlstore"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@ -441,7 +441,7 @@ func TestCanSavePipelinesWithoutConnectedAgents(t *testing.T) {
|
||||
// configuring log pipelines and provides test helpers.
|
||||
type LogPipelinesTestBed struct {
|
||||
t *testing.T
|
||||
testUser *model.User
|
||||
testUser *types.User
|
||||
apiHandler *app.APIHandler
|
||||
agentConfMgr *agentConf.Manager
|
||||
opampServer *opamp.Server
|
||||
|
@ -17,9 +17,9 @@ import (
|
||||
"go.signoz.io/signoz/pkg/query-service/auth"
|
||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||
"go.signoz.io/signoz/pkg/query-service/featureManager"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||
"go.signoz.io/signoz/pkg/sqlstore"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -338,7 +338,7 @@ func TestConfigReturnedWhenAgentChecksIn(t *testing.T) {
|
||||
|
||||
type CloudIntegrationsTestBed struct {
|
||||
t *testing.T
|
||||
testUser *model.User
|
||||
testUser *types.User
|
||||
qsHttpHandler http.Handler
|
||||
mockClickhouse mockhouse.ClickConnMockCommon
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||
"go.signoz.io/signoz/pkg/sqlstore"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -367,7 +368,7 @@ func TestDashboardsForInstalledIntegrationDashboards(t *testing.T) {
|
||||
|
||||
type IntegrationsTestBed struct {
|
||||
t *testing.T
|
||||
testUser *model.User
|
||||
testUser *types.User
|
||||
qsHttpHandler http.Handler
|
||||
mockClickhouse mockhouse.ClickConnMockCommon
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||
"go.signoz.io/signoz/pkg/query-service/interfaces"
|
||||
"go.signoz.io/signoz/pkg/query-service/model"
|
||||
"go.signoz.io/signoz/pkg/types"
|
||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
@ -150,10 +151,10 @@ func makeTestSignozLog(
|
||||
return testLog
|
||||
}
|
||||
|
||||
func createTestUser() (*model.User, *model.ApiError) {
|
||||
func createTestUser() (*types.User, *model.ApiError) {
|
||||
// Create a test user for auth
|
||||
ctx := context.Background()
|
||||
org, apiErr := dao.DB().CreateOrg(ctx, &model.Organization{
|
||||
org, apiErr := dao.DB().CreateOrg(ctx, &types.Organization{
|
||||
Name: "test",
|
||||
})
|
||||
if apiErr != nil {
|
||||
@ -170,20 +171,20 @@ func createTestUser() (*model.User, *model.ApiError) {
|
||||
userId := uuid.NewString()
|
||||
return dao.DB().CreateUser(
|
||||
ctx,
|
||||
&model.User{
|
||||
Id: userId,
|
||||
&types.User{
|
||||
ID: userId,
|
||||
Name: "test",
|
||||
Email: userId[:8] + "test@test.com",
|
||||
Password: "test",
|
||||
OrgId: org.Id,
|
||||
GroupId: group.ID,
|
||||
OrgID: org.ID,
|
||||
GroupID: group.ID,
|
||||
},
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func AuthenticatedRequestForTest(
|
||||
user *model.User,
|
||||
user *types.User,
|
||||
path string,
|
||||
postData interface{},
|
||||
) (*http.Request, error) {
|
||||
|
@ -45,6 +45,9 @@ func NewTestSqliteDB(t *testing.T) (sqlStore sqlstore.SQLStore, testDBFilePath s
|
||||
sqlmigration.NewAddIntegrationsFactory(),
|
||||
sqlmigration.NewAddLicensesFactory(),
|
||||
sqlmigration.NewAddPatsFactory(),
|
||||
sqlmigration.NewModifyDatetimeFactory(),
|
||||
sqlmigration.NewModifyOrgDomainFactory(),
|
||||
sqlmigration.NewUpdateOrganizationFactory(sqlStore),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
@ -62,7 +65,10 @@ func NewTestSqliteDB(t *testing.T) (sqlStore sqlstore.SQLStore, testDBFilePath s
|
||||
func NewQueryServiceDBForTests(t *testing.T) sqlstore.SQLStore {
|
||||
sqlStore, _ := NewTestSqliteDB(t)
|
||||
|
||||
dao.InitDao(sqlStore)
|
||||
err := dao.InitDao(sqlStore)
|
||||
if err != nil {
|
||||
t.Fatalf("could not initialize dao: %v", err)
|
||||
}
|
||||
dashboards.InitDB(sqlStore.SQLxDB())
|
||||
|
||||
return sqlStore
|
||||
|
@ -74,6 +74,9 @@ func New(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add the org migration here since we need to pass the sqlstore
|
||||
providerConfig.SQLMigrationProviderFactories.Add(sqlmigration.NewUpdateOrganizationFactory(sqlstore))
|
||||
|
||||
// Initialize telemetrystore from the available telemetrystore provider factories
|
||||
telemetrystore, err := factory.NewProviderFromNamedMap(
|
||||
ctx,
|
||||
|
@ -35,7 +35,7 @@ func (migration *addPats) Up(ctx context.Context, db *bun.DB) error {
|
||||
OrgID string `bun:"org_id,type:text,notnull"`
|
||||
Name string `bun:"name,type:varchar(50),notnull,unique"`
|
||||
CreatedAt int `bun:"created_at,notnull"`
|
||||
UpdatedAt int `bun:"updated_at,type:timestamp"`
|
||||
UpdatedAt int `bun:"updated_at"`
|
||||
Data string `bun:"data,type:text,notnull"`
|
||||
}{}).
|
||||
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/dialect"
|
||||
"github.com/uptrace/bun/migrate"
|
||||
"go.signoz.io/signoz/pkg/factory"
|
||||
)
|
||||
@ -27,6 +28,11 @@ func (migration *modifyOrgDomain) Register(migrations *migrate.Migrations) error
|
||||
}
|
||||
|
||||
func (migration *modifyOrgDomain) Up(ctx context.Context, db *bun.DB) error {
|
||||
// only run this for old sqlite db
|
||||
if db.Dialect().Name() != dialect.SQLite {
|
||||
return nil
|
||||
}
|
||||
|
||||
// begin transaction
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
|
152
pkg/sqlmigration/013_update_organization.go
Normal file
152
pkg/sqlmigration/013_update_organization.go
Normal file
@ -0,0 +1,152 @@
|
||||
package sqlmigration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/migrate"
|
||||
"go.signoz.io/signoz/pkg/factory"
|
||||
"go.signoz.io/signoz/pkg/sqlstore"
|
||||
)
|
||||
|
||||
type updateOrganization struct {
|
||||
store sqlstore.SQLStore
|
||||
}
|
||||
|
||||
func NewUpdateOrganizationFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("update_organization"), func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
|
||||
return newUpdateOrganization(ctx, ps, c, sqlstore)
|
||||
})
|
||||
}
|
||||
|
||||
func newUpdateOrganization(_ context.Context, _ factory.ProviderSettings, _ Config, store sqlstore.SQLStore) (SQLMigration, error) {
|
||||
return &updateOrganization{
|
||||
store: store,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (migration *updateOrganization) Register(migrations *migrate.Migrations) error {
|
||||
if err := migrations.Register(migration.Up, migration.Down); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (migration *updateOrganization) Up(ctx context.Context, db *bun.DB) error {
|
||||
|
||||
// begin transaction
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// update apdex settings table
|
||||
if err := updateApdexSettings(ctx, tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// drop user_flags table
|
||||
if _, err := tx.NewDropTable().IfExists().Table("user_flags").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add org id to groups table
|
||||
if exists, err := migration.store.Dialect().ColumnExists(ctx, tx, "groups", "org_id"); err != nil {
|
||||
return err
|
||||
} else if !exists {
|
||||
if _, err := tx.NewAddColumn().Table("groups").ColumnExpr("org_id TEXT").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add created_at to groups table
|
||||
for _, table := range []string{"groups"} {
|
||||
if exists, err := migration.store.Dialect().ColumnExists(ctx, tx, table, "created_at"); err != nil {
|
||||
return err
|
||||
} else if !exists {
|
||||
if _, err := tx.NewAddColumn().Table(table).ColumnExpr("created_at TIMESTAMP").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add updated_at to organizations, users, groups table
|
||||
for _, table := range []string{"organizations", "users", "groups"} {
|
||||
if exists, err := migration.store.Dialect().ColumnExists(ctx, tx, table, "updated_at"); err != nil {
|
||||
return err
|
||||
} else if !exists {
|
||||
if _, err := tx.NewAddColumn().Table(table).ColumnExpr("updated_at TIMESTAMP").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// since organizations, users has created_at as integer instead of timestamp
|
||||
for _, table := range []string{"organizations", "users", "invites"} {
|
||||
if err := migration.store.Dialect().MigrateIntToTimestamp(ctx, tx, table, "created_at"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// migrate is_anonymous and has_opted_updates to boolean from int
|
||||
for _, column := range []string{"is_anonymous", "has_opted_updates"} {
|
||||
if err := migration.store.Dialect().MigrateIntToBoolean(ctx, tx, "organizations", column); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (migration *updateOrganization) Down(ctx context.Context, db *bun.DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateApdexSettings(ctx context.Context, tx bun.Tx) error {
|
||||
if _, err := tx.NewCreateTable().
|
||||
Model(&struct {
|
||||
bun.BaseModel `bun:"table:apdex_settings_new"`
|
||||
OrgID string `bun:"org_id,pk,type:text"`
|
||||
ServiceName string `bun:"service_name,pk,type:text"`
|
||||
Threshold float64 `bun:"threshold,type:float,notnull"`
|
||||
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull"`
|
||||
}{}).
|
||||
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
|
||||
IfNotExists().
|
||||
Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get org id from organizations table
|
||||
var orgID string
|
||||
if err := tx.QueryRowContext(ctx, `SELECT id FROM organizations LIMIT 1`).Scan(&orgID); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
|
||||
if orgID != "" {
|
||||
// copy old data
|
||||
if _, err := tx.ExecContext(ctx, `INSERT INTO apdex_settings_new (org_id, service_name, threshold, exclude_status_codes) SELECT ?, service_name, threshold, exclude_status_codes FROM apdex_settings`, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// drop old table
|
||||
if _, err := tx.NewDropTable().IfExists().Table("apdex_settings").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rename new table to old table
|
||||
if _, err := tx.ExecContext(ctx, `ALTER TABLE apdex_settings_new RENAME TO apdex_settings`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -86,3 +86,29 @@ func WrapIfNotExists(ctx context.Context, db *bun.DB, table string, column strin
|
||||
return q.Err(ErrNoExecute)
|
||||
}
|
||||
}
|
||||
|
||||
func GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error) {
|
||||
var columnType string
|
||||
var err error
|
||||
|
||||
if bun.Dialect().Name() == dialect.SQLite {
|
||||
err = bun.NewSelect().
|
||||
ColumnExpr("type").
|
||||
TableExpr("pragma_table_info(?)", table).
|
||||
Where("name = ?", column).
|
||||
Scan(ctx, &columnType)
|
||||
} else {
|
||||
err = bun.NewSelect().
|
||||
ColumnExpr("data_type").
|
||||
TableExpr("information_schema.columns").
|
||||
Where("table_name = ?", table).
|
||||
Where("column_name = ?", column).
|
||||
Scan(ctx, &columnType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return columnType, nil
|
||||
}
|
||||
|
116
pkg/sqlstore/postgressqlstore/dialect.go
Normal file
116
pkg/sqlstore/postgressqlstore/dialect.go
Normal file
@ -0,0 +1,116 @@
|
||||
package postgressqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type PGDialect struct {
|
||||
}
|
||||
|
||||
func (dialect *PGDialect) MigrateIntToTimestamp(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// bigint for postgres and INTEGER for sqlite
|
||||
if columnType != "bigint" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if the columns is integer then do this
|
||||
if _, err := bun.ExecContext(ctx, `ALTER TABLE `+table+` RENAME COLUMN `+column+` TO `+column+`_old`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add new timestamp column
|
||||
if _, err := bun.NewAddColumn().Table(table).ColumnExpr(column + " TIMESTAMP").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := bun.NewUpdate().
|
||||
Table(table).
|
||||
Set(column + " = to_timestamp(cast(" + column + "_old as INTEGER))").
|
||||
Where("1=1").
|
||||
Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// drop old column
|
||||
if _, err := bun.NewDropColumn().Table(table).Column(column + "_old").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dialect *PGDialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if columnType != "bigint" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := bun.ExecContext(ctx, `ALTER TABLE `+table+` RENAME COLUMN `+column+` TO `+column+`_old`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add new boolean column
|
||||
if _, err := bun.NewAddColumn().Table(table).ColumnExpr(column + " BOOLEAN").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// copy data from old column to new column, converting from int to boolean
|
||||
if _, err := bun.NewUpdate().
|
||||
Table(table).
|
||||
Set(column + " = CASE WHEN " + column + "_old = 1 THEN true ELSE false END").
|
||||
Where("1=1").
|
||||
Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// drop old column
|
||||
if _, err := bun.NewDropColumn().Table(table).Column(column + "_old").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dialect *PGDialect) GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error) {
|
||||
var columnType string
|
||||
var err error
|
||||
err = bun.NewSelect().
|
||||
ColumnExpr("data_type").
|
||||
TableExpr("information_schema.columns").
|
||||
Where("table_name = ?", table).
|
||||
Where("column_name = ?", column).
|
||||
Scan(ctx, &columnType)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return columnType, nil
|
||||
}
|
||||
|
||||
func (dialect *PGDialect) ColumnExists(ctx context.Context, bun bun.IDB, table string, column string) (bool, error) {
|
||||
var count int
|
||||
err := bun.NewSelect().
|
||||
ColumnExpr("COUNT(*)").
|
||||
TableExpr("information_schema.columns").
|
||||
Where("table_name = ?", table).
|
||||
Where("column_name = ?", column).
|
||||
Scan(ctx, &count)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
@ -18,6 +18,7 @@ type provider struct {
|
||||
sqldb *sql.DB
|
||||
bundb *sqlstore.BunDB
|
||||
sqlxdb *sqlx.DB
|
||||
dialect *PGDialect
|
||||
}
|
||||
|
||||
func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] {
|
||||
@ -59,6 +60,7 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
|
||||
sqldb: sqldb,
|
||||
bundb: sqlstore.NewBunDB(settings, sqldb, pgdialect.New(), hooks),
|
||||
sqlxdb: sqlx.NewDb(sqldb, "postgres"),
|
||||
dialect: &PGDialect{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -74,6 +76,10 @@ func (provider *provider) SQLxDB() *sqlx.DB {
|
||||
return provider.sqlxdb
|
||||
}
|
||||
|
||||
func (provider *provider) Dialect() sqlstore.SQLDialect {
|
||||
return provider.dialect
|
||||
}
|
||||
|
||||
func (provider *provider) BunDBCtx(ctx context.Context) bun.IDB {
|
||||
return provider.bundb.BunDBCtx(ctx)
|
||||
}
|
||||
|
115
pkg/sqlstore/sqlitesqlstore/dialect.go
Normal file
115
pkg/sqlstore/sqlitesqlstore/dialect.go
Normal file
@ -0,0 +1,115 @@
|
||||
package sqlitesqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type SQLiteDialect struct {
|
||||
}
|
||||
|
||||
func (dialect *SQLiteDialect) MigrateIntToTimestamp(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if columnType != "INTEGER" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if the columns is integer then do this
|
||||
if _, err := bun.ExecContext(ctx, `ALTER TABLE `+table+` RENAME COLUMN `+column+` TO `+column+`_old`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add new timestamp column
|
||||
if _, err := bun.NewAddColumn().Table(table).ColumnExpr(column + " TIMESTAMP").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// copy data from old column to new column, converting from int (unix timestamp) to timestamp
|
||||
if _, err := bun.NewUpdate().
|
||||
Table(table).
|
||||
Set(column + " = datetime(" + column + "_old, 'unixepoch')").
|
||||
Where("1=1").
|
||||
Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// drop old column
|
||||
if _, err := bun.NewDropColumn().Table(table).Column(column + "_old").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dialect *SQLiteDialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if columnType != "INTEGER" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := bun.ExecContext(ctx, `ALTER TABLE `+table+` RENAME COLUMN `+column+` TO `+column+`_old`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add new boolean column
|
||||
if _, err := bun.NewAddColumn().Table(table).ColumnExpr(column + " BOOLEAN").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// copy data from old column to new column, converting from int to boolean
|
||||
if _, err := bun.NewUpdate().
|
||||
Table(table).
|
||||
Set(column + " = CASE WHEN " + column + "_old = 1 THEN true ELSE false END").
|
||||
Where("1=1").
|
||||
Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// drop old column
|
||||
if _, err := bun.NewDropColumn().Table(table).Column(column + "_old").Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dialect *SQLiteDialect) GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error) {
|
||||
var columnType string
|
||||
var err error
|
||||
|
||||
err = bun.NewSelect().
|
||||
ColumnExpr("type").
|
||||
TableExpr("pragma_table_info(?)", table).
|
||||
Where("name = ?", column).
|
||||
Scan(ctx, &columnType)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return columnType, nil
|
||||
}
|
||||
|
||||
func (dialect *SQLiteDialect) ColumnExists(ctx context.Context, bun bun.IDB, table string, column string) (bool, error) {
|
||||
var count int
|
||||
err := bun.NewSelect().
|
||||
ColumnExpr("COUNT(*)").
|
||||
TableExpr("pragma_table_info(?)", table).
|
||||
Where("name = ?", column).
|
||||
Scan(ctx, &count)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
@ -17,6 +17,7 @@ type provider struct {
|
||||
sqldb *sql.DB
|
||||
bundb *sqlstore.BunDB
|
||||
sqlxdb *sqlx.DB
|
||||
dialect *SQLiteDialect
|
||||
}
|
||||
|
||||
func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] {
|
||||
@ -49,6 +50,7 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
|
||||
sqldb: sqldb,
|
||||
bundb: sqlstore.NewBunDB(settings, sqldb, sqlitedialect.New(), hooks),
|
||||
sqlxdb: sqlx.NewDb(sqldb, "sqlite3"),
|
||||
dialect: &SQLiteDialect{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -64,6 +66,10 @@ func (provider *provider) SQLxDB() *sqlx.DB {
|
||||
return provider.sqlxdb
|
||||
}
|
||||
|
||||
func (provider *provider) Dialect() sqlstore.SQLDialect {
|
||||
return provider.dialect
|
||||
}
|
||||
|
||||
func (provider *provider) BunDBCtx(ctx context.Context) bun.IDB {
|
||||
return provider.bundb.BunDBCtx(ctx)
|
||||
}
|
||||
|
@ -20,6 +20,9 @@ type SQLStore interface {
|
||||
// SQLxDB returns an instance of sqlx.DB. This is the legacy ORM used.
|
||||
SQLxDB() *sqlx.DB
|
||||
|
||||
// Returns the dialect of the database.
|
||||
Dialect() SQLDialect
|
||||
|
||||
// RunInTxCtx runs the given callback in a transaction. It creates and injects a new context with the transaction.
|
||||
// If a transaction is present in the context, it will be used.
|
||||
RunInTxCtx(ctx context.Context, opts *SQLStoreTxOptions, cb func(ctx context.Context) error) error
|
||||
@ -32,3 +35,10 @@ type SQLStore interface {
|
||||
type SQLStoreHook interface {
|
||||
bun.QueryHook
|
||||
}
|
||||
|
||||
type SQLDialect interface {
|
||||
MigrateIntToTimestamp(ctx context.Context, bun bun.IDB, table string, column string) error
|
||||
MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error
|
||||
GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error)
|
||||
ColumnExists(ctx context.Context, bun bun.IDB, table string, column string) (bool, error)
|
||||
}
|
||||
|
26
pkg/sqlstore/sqlstoretest/dialect.go
Normal file
26
pkg/sqlstore/sqlstoretest/dialect.go
Normal file
@ -0,0 +1,26 @@
|
||||
package sqlstoretest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type TestDialect struct {
|
||||
}
|
||||
|
||||
func (dialect *TestDialect) MigrateIntToTimestamp(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dialect *TestDialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dialect *TestDialect) GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (dialect *TestDialect) ColumnExists(ctx context.Context, bun bun.IDB, table string, column string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
@ -19,6 +19,7 @@ type Provider struct {
|
||||
mock sqlmock.Sqlmock
|
||||
bunDB *bun.DB
|
||||
sqlxDB *sqlx.DB
|
||||
dialect *TestDialect
|
||||
}
|
||||
|
||||
func New(config sqlstore.Config, matcher sqlmock.QueryMatcher) *Provider {
|
||||
@ -42,6 +43,7 @@ func New(config sqlstore.Config, matcher sqlmock.QueryMatcher) *Provider {
|
||||
mock: mock,
|
||||
bunDB: bunDB,
|
||||
sqlxDB: sqlxDB,
|
||||
dialect: &TestDialect{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,6 +63,10 @@ func (provider *Provider) Mock() sqlmock.Sqlmock {
|
||||
return provider.mock
|
||||
}
|
||||
|
||||
func (provider *Provider) Dialect() sqlstore.SQLDialect {
|
||||
return provider.dialect
|
||||
}
|
||||
|
||||
func (provider *Provider) BunDBCtx(ctx context.Context) bun.IDB {
|
||||
return provider.bunDB
|
||||
}
|
||||
|
13
pkg/types/auditable.go
Normal file
13
pkg/types/auditable.go
Normal file
@ -0,0 +1,13 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
type TimeAuditable struct {
|
||||
CreatedAt time.Time `bun:"created_at" json:"createdAt"`
|
||||
UpdatedAt time.Time `bun:"updated_at" json:"updatedAt"`
|
||||
}
|
||||
|
||||
type UserAuditable struct {
|
||||
CreatedBy string `bun:"created_by" json:"createdBy"`
|
||||
UpdatedBy string `bun:"updated_by" json:"updatedBy"`
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
@ -10,69 +8,16 @@ import (
|
||||
type Organization struct {
|
||||
bun.BaseModel `bun:"table:organizations"`
|
||||
|
||||
ID string `bun:"id,pk,type:text"`
|
||||
Name string `bun:"name,type:text,notnull"`
|
||||
CreatedAt int `bun:"created_at,notnull"`
|
||||
IsAnonymous int `bun:"is_anonymous,notnull,default:0,CHECK(is_anonymous IN (0,1))"`
|
||||
HasOptedUpdates int `bun:"has_opted_updates,notnull,default:1,CHECK(has_opted_updates IN (0,1))"`
|
||||
}
|
||||
|
||||
type Invite struct {
|
||||
bun.BaseModel `bun:"table:invites"`
|
||||
|
||||
ID int `bun:"id,pk,autoincrement"`
|
||||
Name string `bun:"name,type:text,notnull"`
|
||||
Email string `bun:"email,type:text,notnull,unique"`
|
||||
Token string `bun:"token,type:text,notnull"`
|
||||
CreatedAt int `bun:"created_at,notnull"`
|
||||
Role string `bun:"role,type:text,notnull"`
|
||||
OrgID string `bun:"org_id,type:text,notnull"`
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
bun.BaseModel `bun:"table:groups"`
|
||||
TimeAuditable
|
||||
ID string `bun:"id,pk,type:text" json:"id"`
|
||||
Name string `bun:"name,type:text,notnull,unique" json:"name"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
bun.BaseModel `bun:"table:users"`
|
||||
ID string `bun:"id,pk,type:text"`
|
||||
Name string `bun:"name,type:text,notnull"`
|
||||
Email string `bun:"email,type:text,notnull,unique"`
|
||||
Password string `bun:"password,type:text,notnull"`
|
||||
CreatedAt int `bun:"created_at,notnull"`
|
||||
ProfilePictureURL string `bun:"profile_picture_url,type:text"`
|
||||
GroupID string `bun:"group_id,type:text,notnull"`
|
||||
OrgID string `bun:"org_id,type:text,notnull"`
|
||||
}
|
||||
|
||||
type ResetPasswordRequest struct {
|
||||
bun.BaseModel `bun:"table:reset_password_request"`
|
||||
ID int `bun:"id,pk,autoincrement"`
|
||||
Token string `bun:"token,type:text,notnull"`
|
||||
UserID string `bun:"user_id,type:text,notnull"`
|
||||
}
|
||||
|
||||
type UserFlags struct {
|
||||
bun.BaseModel `bun:"table:user_flags"`
|
||||
UserID string `bun:"user_id,pk,type:text,notnull"`
|
||||
Flags string `bun:"flags,type:text"`
|
||||
Name string `bun:"name,type:text,notnull" json:"name"`
|
||||
IsAnonymous bool `bun:"is_anonymous,notnull,default:0,CHECK(is_anonymous IN (0,1))" json:"isAnonymous"`
|
||||
HasOptedUpdates bool `bun:"has_opted_updates,notnull,default:1,CHECK(has_opted_updates IN (0,1))" json:"hasOptedUpdates"`
|
||||
}
|
||||
|
||||
type ApdexSettings struct {
|
||||
bun.BaseModel `bun:"table:apdex_settings"`
|
||||
ServiceName string `bun:"service_name,pk,type:text"`
|
||||
Threshold float64 `bun:"threshold,type:float,notnull"`
|
||||
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull"`
|
||||
}
|
||||
|
||||
type IngestionKey struct {
|
||||
bun.BaseModel `bun:"table:ingestion_keys"`
|
||||
KeyId string `bun:"key_id,pk,type:text"`
|
||||
Name string `bun:"name,type:text"`
|
||||
CreatedAt time.Time `bun:"created_at,default:current_timestamp"`
|
||||
IngestionKey string `bun:"ingestion_key,type:text,notnull"`
|
||||
IngestionURL string `bun:"ingestion_url,type:text,notnull"`
|
||||
DataRegion string `bun:"data_region,type:text,notnull"`
|
||||
OrgID string `bun:"org_id,pk,type:text" json:"orgId"`
|
||||
ServiceName string `bun:"service_name,pk,type:text" json:"serviceName"`
|
||||
Threshold float64 `bun:"threshold,type:float,notnull" json:"threshold"`
|
||||
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull" json:"excludeStatusCodes"`
|
||||
}
|
||||
|
54
pkg/types/user.go
Normal file
54
pkg/types/user.go
Normal file
@ -0,0 +1,54 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type Invite struct {
|
||||
bun.BaseModel `bun:"table:invites"`
|
||||
|
||||
OrgID string `bun:"org_id,type:text,notnull" json:"orgId"`
|
||||
ID int `bun:"id,pk,autoincrement" json:"id"`
|
||||
Name string `bun:"name,type:text,notnull" json:"name"`
|
||||
Email string `bun:"email,type:text,notnull,unique" json:"email"`
|
||||
Token string `bun:"token,type:text,notnull" json:"token"`
|
||||
CreatedAt time.Time `bun:"created_at,notnull" json:"createdAt"`
|
||||
Role string `bun:"role,type:text,notnull" json:"role"`
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
bun.BaseModel `bun:"table:groups"`
|
||||
|
||||
TimeAuditable
|
||||
OrgID string `bun:"org_id,type:text"`
|
||||
ID string `bun:"id,pk,type:text" json:"id"`
|
||||
Name string `bun:"name,type:text,notnull,unique" json:"name"`
|
||||
}
|
||||
|
||||
type GettableUser struct {
|
||||
User
|
||||
Role string `json:"role"`
|
||||
Organization string `json:"organization"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
bun.BaseModel `bun:"table:users"`
|
||||
|
||||
TimeAuditable
|
||||
ID string `bun:"id,pk,type:text" json:"id"`
|
||||
Name string `bun:"name,type:text,notnull" json:"name"`
|
||||
Email string `bun:"email,type:text,notnull,unique" json:"email"`
|
||||
Password string `bun:"password,type:text,notnull" json:"-"`
|
||||
ProfilePictureURL string `bun:"profile_picture_url,type:text" json:"profilePictureURL"`
|
||||
GroupID string `bun:"group_id,type:text,notnull" json:"groupId"`
|
||||
OrgID string `bun:"org_id,type:text,notnull" json:"orgId"`
|
||||
}
|
||||
|
||||
type ResetPasswordRequest struct {
|
||||
bun.BaseModel `bun:"table:reset_password_request"`
|
||||
ID int `bun:"id,pk,autoincrement" json:"id"`
|
||||
Token string `bun:"token,type:text,notnull" json:"token"`
|
||||
UserID string `bun:"user_id,type:text,notnull" json:"userId"`
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user