mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-11 16:09:03 +08:00
feat(organization): schema changes for the organizations entity (#7684)
* feat(organization): add hname and alias for organization * fix: boolean values are not shown in the list panel's column * fix: moved logic to component level * fix: added type * fix: added test cases * fix: added test cases * chore: update copy webpack plugin * Revert "fix: display same key with multiple data types in filter suggestions by enhancing the deduping logic (#7255)" This reverts commit 1e85981a17a8e715e948308d3e85072d976907d3. * fix: use query search v2 for traces data source to handle multiple data types for the same key * fix(QueryBuilderSearchV2): add user typed option if it doesn't exist in the payload * fix(QueryBuilderSearchV2): increase the height of search dropdown for non-logs data sources * fix: display span scope selector for trace data source * chore: remove the span scope selector from qb search v1 and move the component to search v2 * fix: write test to ensure that we display span scope selector for traces data source * fix: limit converting -> only to log data source * fix: don't display empty suggestion if only spaces are typed * chore: tests for span scope selector * chore: qb search flow (key, operator, value) test cases * refactor: fix the Maximum update depth reached issue while running tests * chore: overall improvements to span scope selector tests Resource attr filter: style fix and quick filter changes (#7691) * chore: resource attr filter init * chore: resource attr filter api integration * chore: operator config updated * chore: fliter show hide logic and styles * chore: add support for custom operator list to qb * chore: minor refactor * chore: minor code refactor * test: quick filters test suite added * test: quick filters test suite added * test: all errors test suite added * chore: style fix * test: all errors mock fix * chore: test case fix and mixpanel update * chore: color update * chore: minor refactor * chore: style fix * chore: set default query in exceptions tab * chore: style fix * chore: minor refactor * chore: minor refactor * chore: minor refactor * chore: test update * chore: fix filter header with no query name * fix: scroll fix * chore: add data source traces to quick filters * chore: replace div with fragment --------- Co-authored-by: Aditya Singh <adityasingh@Adityas-MacBook-Pro.local> fix: handle rate operators for table panel (#7695) * fix: handle rate operators for table panel chore: fix error rate (#7701) Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com> * feat(organization): minor cleanups * feat(organization): better naming for api and usecase * feat(organization): better packaging for modules * feat(organization): change hname to displayName * feat(organization): update the migration to use dialect * feat(organization): update the migration to use dialect * feat(organization): update the migration to use dialect * feat(organization): revert back to impl * feat(organization): remove DI from organization * feat(organization): address review comments * feat(organization): address review comments * feat(organization): address review comments --------- Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com>
This commit is contained in:
parent
a1846c008a
commit
c322657666
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/ee/query-service/usage"
|
"github.com/SigNoz/signoz/ee/query-service/usage"
|
||||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||||
"github.com/SigNoz/signoz/pkg/apis/fields"
|
"github.com/SigNoz/signoz/pkg/apis/fields"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/preference"
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
preferencecore "github.com/SigNoz/signoz/pkg/modules/preference/core"
|
preferencecore "github.com/SigNoz/signoz/pkg/modules/preference/core"
|
||||||
baseapp "github.com/SigNoz/signoz/pkg/query-service/app"
|
baseapp "github.com/SigNoz/signoz/pkg/query-service/app"
|
||||||
@ -59,6 +60,8 @@ type APIHandler struct {
|
|||||||
// NewAPIHandler returns an APIHandler
|
// NewAPIHandler returns an APIHandler
|
||||||
func NewAPIHandler(opts APIHandlerOptions, signoz *signoz.SigNoz) (*APIHandler, error) {
|
func NewAPIHandler(opts APIHandlerOptions, signoz *signoz.SigNoz) (*APIHandler, error) {
|
||||||
preference := preference.NewAPI(preferencecore.NewPreference(preferencecore.NewStore(signoz.SQLStore), preferencetypes.NewDefaultPreferenceMap()))
|
preference := preference.NewAPI(preferencecore.NewPreference(preferencecore.NewStore(signoz.SQLStore), preferencetypes.NewDefaultPreferenceMap()))
|
||||||
|
organizationAPI := implorganization.NewAPI(implorganization.NewModule(implorganization.NewStore(signoz.SQLStore)))
|
||||||
|
organizationModule := implorganization.NewModule(implorganization.NewStore(signoz.SQLStore))
|
||||||
|
|
||||||
baseHandler, err := baseapp.NewAPIHandler(baseapp.APIHandlerOpts{
|
baseHandler, err := baseapp.NewAPIHandler(baseapp.APIHandlerOpts{
|
||||||
Reader: opts.DataConnector,
|
Reader: opts.DataConnector,
|
||||||
@ -78,6 +81,8 @@ func NewAPIHandler(opts APIHandlerOptions, signoz *signoz.SigNoz) (*APIHandler,
|
|||||||
FieldsAPI: fields.NewAPI(signoz.TelemetryStore),
|
FieldsAPI: fields.NewAPI(signoz.TelemetryStore),
|
||||||
Signoz: signoz,
|
Signoz: signoz,
|
||||||
Preference: preference,
|
Preference: preference,
|
||||||
|
OrganizationAPI: organizationAPI,
|
||||||
|
OrganizationModule: organizationModule,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -134,7 +134,7 @@ func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, registerError := baseauth.Register(ctx, req, ah.Signoz.Alertmanager)
|
_, registerError := baseauth.Register(ctx, req, ah.Signoz.Alertmanager, ah.OrganizationModule)
|
||||||
if !registerError.IsNil() {
|
if !registerError.IsNil() {
|
||||||
RespondError(w, apierr, nil)
|
RespondError(w, apierr, nil)
|
||||||
return
|
return
|
||||||
@ -151,9 +151,8 @@ func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (ah *APIHandler) getInvite(w http.ResponseWriter, r *http.Request) {
|
func (ah *APIHandler) getInvite(w http.ResponseWriter, r *http.Request) {
|
||||||
token := mux.Vars(r)["token"]
|
token := mux.Vars(r)["token"]
|
||||||
sourceUrl := r.URL.Query().Get("ref")
|
sourceUrl := r.URL.Query().Get("ref")
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
inviteObject, err := baseauth.GetInvite(context.Background(), token)
|
inviteObject, err := baseauth.GetInvite(r.Context(), token, ah.OrganizationModule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
RespondError(w, model.BadRequest(err), nil)
|
RespondError(w, model.BadRequest(err), nil)
|
||||||
return
|
return
|
||||||
@ -163,7 +162,7 @@ func (ah *APIHandler) getInvite(w http.ResponseWriter, r *http.Request) {
|
|||||||
InvitationResponseObject: inviteObject,
|
InvitationResponseObject: inviteObject,
|
||||||
}
|
}
|
||||||
|
|
||||||
precheck, apierr := ah.AppDao().PrecheckLogin(ctx, inviteObject.Email, sourceUrl)
|
precheck, apierr := ah.AppDao().PrecheckLogin(r.Context(), inviteObject.Email, sourceUrl)
|
||||||
resp.Precheck = precheck
|
resp.Precheck = precheck
|
||||||
|
|
||||||
if apierr != nil {
|
if apierr != nil {
|
||||||
|
@ -9,10 +9,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/ee/query-service/constants"
|
"github.com/SigNoz/signoz/ee/query-service/constants"
|
||||||
"github.com/SigNoz/signoz/ee/query-service/model"
|
"github.com/SigNoz/signoz/ee/query-service/model"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var C *Client
|
var C *Client
|
||||||
|
@ -138,15 +138,6 @@ func (lm *Manager) UploadUsage() {
|
|||||||
|
|
||||||
zap.L().Info("uploading usage data")
|
zap.L().Info("uploading usage data")
|
||||||
|
|
||||||
orgName := ""
|
|
||||||
orgNames, orgError := lm.modelDao.GetOrgs(ctx)
|
|
||||||
if orgError != nil {
|
|
||||||
zap.L().Error("failed to get org data: %v", zap.Error(orgError))
|
|
||||||
}
|
|
||||||
if len(orgNames) == 1 {
|
|
||||||
orgName = orgNames[0].Name
|
|
||||||
}
|
|
||||||
|
|
||||||
usagesPayload := []model.Usage{}
|
usagesPayload := []model.Usage{}
|
||||||
for _, usage := range usages {
|
for _, usage := range usages {
|
||||||
usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data))
|
usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data))
|
||||||
@ -166,7 +157,7 @@ func (lm *Manager) UploadUsage() {
|
|||||||
usageData.ExporterID = usage.ExporterID
|
usageData.ExporterID = usage.ExporterID
|
||||||
usageData.Type = usage.Type
|
usageData.Type = usage.Type
|
||||||
usageData.Tenant = "default"
|
usageData.Tenant = "default"
|
||||||
usageData.OrgName = orgName
|
usageData.OrgName = "default"
|
||||||
usageData.TenantId = lm.tenantID
|
usageData.TenantId = lm.tenantID
|
||||||
usagesPayload = append(usagesPayload, usageData)
|
usagesPayload = append(usagesPayload, usageData)
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,14 @@ func (dialect *dialect) MigrateIntToTimestamp(ctx context.Context, bun bun.IDB,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dialect *dialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
func (dialect *dialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
columnExists, err := dialect.ColumnExists(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !columnExists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -151,6 +159,26 @@ func (dialect *dialect) ColumnExists(ctx context.Context, bun bun.IDB, table str
|
|||||||
return count > 0, nil
|
return count > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dialect *dialect) AddColumn(ctx context.Context, bun bun.IDB, table string, column string, columnExpr string) error {
|
||||||
|
exists, err := dialect.ColumnExists(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
_, err = bun.
|
||||||
|
NewAddColumn().
|
||||||
|
Table(table).
|
||||||
|
ColumnExpr(column + " " + columnExpr).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table string, oldColumnName string, newColumnName string) (bool, error) {
|
func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table string, oldColumnName string, newColumnName string) (bool, error) {
|
||||||
oldColumnExists, err := dialect.ColumnExists(ctx, bun, table, oldColumnName)
|
oldColumnExists, err := dialect.ColumnExists(ctx, bun, table, oldColumnName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -162,10 +190,14 @@ func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table str
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !oldColumnExists && newColumnExists {
|
if newColumnExists {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !oldColumnExists {
|
||||||
|
return false, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("old column: %s doesn't exist", oldColumnName))
|
||||||
|
}
|
||||||
|
|
||||||
_, err = bun.
|
_, err = bun.
|
||||||
ExecContext(ctx, "ALTER TABLE "+table+" RENAME COLUMN "+oldColumnName+" TO "+newColumnName)
|
ExecContext(ctx, "ALTER TABLE "+table+" RENAME COLUMN "+oldColumnName+" TO "+newColumnName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -174,6 +206,26 @@ func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table str
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dialect *dialect) DropColumn(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
exists, err := dialect.ColumnExists(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
_, err = bun.
|
||||||
|
NewDropColumn().
|
||||||
|
Table(table).
|
||||||
|
Column(column).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dialect *dialect) TableExists(ctx context.Context, bun bun.IDB, table interface{}) (bool, error) {
|
func (dialect *dialect) TableExists(ctx context.Context, bun bun.IDB, table interface{}) (bool, error) {
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
|
@ -64,7 +64,7 @@ function App(): JSX.Element {
|
|||||||
// wait for the required data to be loaded before doing init for anything!
|
// wait for the required data to be loaded before doing init for anything!
|
||||||
if (!isFetchingActiveLicenseV3 && activeLicenseV3 && org) {
|
if (!isFetchingActiveLicenseV3 && activeLicenseV3 && org) {
|
||||||
const orgName =
|
const orgName =
|
||||||
org && Array.isArray(org) && org.length > 0 ? org[0].name : '';
|
org && Array.isArray(org) && org.length > 0 ? org[0].displayName : '';
|
||||||
|
|
||||||
const { name, email, role } = user;
|
const { name, email, role } = user;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import axios from 'api';
|
import { ApiV2Instance as axios } from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
@ -8,14 +8,12 @@ const editOrg = async (
|
|||||||
props: Props,
|
props: Props,
|
||||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.put(`/org/${props.orgId}`, {
|
const response = await axios.put(`/orgs/me`, {
|
||||||
name: props.name,
|
displayName: props.displayName,
|
||||||
isAnonymous: props.isAnonymous,
|
|
||||||
hasOptedUpdates: props.hasOptedUpdates,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 204,
|
||||||
error: null,
|
error: null,
|
||||||
message: response.data.status,
|
message: response.data.status,
|
||||||
payload: response.data,
|
payload: response.data,
|
||||||
|
@ -160,7 +160,7 @@ export default function CustomDomainSettings(): JSX.Element {
|
|||||||
{!isLoadingDeploymentsData && (
|
{!isLoadingDeploymentsData && (
|
||||||
<Card className="custom-domain-settings-card">
|
<Card className="custom-domain-settings-card">
|
||||||
<div className="custom-domain-settings-content-header">
|
<div className="custom-domain-settings-content-header">
|
||||||
Team {org?.[0]?.name} Information
|
Team {org?.[0]?.displayName} Information
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="custom-domain-settings-content-body">
|
<div className="custom-domain-settings-content-body">
|
||||||
|
@ -13,8 +13,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
export interface OrgData {
|
export interface OrgData {
|
||||||
id: string;
|
id: string;
|
||||||
isAnonymous: boolean;
|
displayName: string;
|
||||||
name: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrgDetails {
|
export interface OrgDetails {
|
||||||
@ -110,15 +109,14 @@ function OrgQuestions({
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const { statusCode, error } = await editOrg({
|
const { statusCode, error } = await editOrg({
|
||||||
isAnonymous: currentOrgData.isAnonymous,
|
displayName: organisationName,
|
||||||
name: organisationName,
|
|
||||||
orgId: currentOrgData.id,
|
orgId: currentOrgData.id,
|
||||||
});
|
});
|
||||||
if (statusCode === 200) {
|
if (statusCode === 204) {
|
||||||
updateOrg(currentOrgData?.id, orgDetails.organisationName);
|
updateOrg(currentOrgData?.id, organisationName);
|
||||||
|
|
||||||
logEvent('Org Onboarding: Org Name Updated', {
|
logEvent('Org Onboarding: Org Name Updated', {
|
||||||
organisationName: orgDetails.organisationName,
|
organisationName,
|
||||||
});
|
});
|
||||||
|
|
||||||
logEvent('Org Onboarding: Answered', {
|
logEvent('Org Onboarding: Answered', {
|
||||||
|
@ -94,7 +94,7 @@ function OnboardingQuestionaire(): JSX.Element {
|
|||||||
|
|
||||||
setOrgDetails({
|
setOrgDetails({
|
||||||
...orgDetails,
|
...orgDetails,
|
||||||
organisationName: org[0].name,
|
organisationName: org[0].displayName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
@ -390,7 +390,7 @@ function OnboardingAddDataSource(): JSX.Element {
|
|||||||
setSetupStepItems([
|
setSetupStepItems([
|
||||||
{
|
{
|
||||||
...setupStepItemsBase[0],
|
...setupStepItemsBase[0],
|
||||||
description: org?.[0]?.name || '',
|
description: org?.[0]?.displayName || '',
|
||||||
},
|
},
|
||||||
...setupStepItemsBase.slice(1),
|
...setupStepItemsBase.slice(1),
|
||||||
]);
|
]);
|
||||||
@ -403,7 +403,7 @@ function OnboardingAddDataSource(): JSX.Element {
|
|||||||
setSetupStepItems([
|
setSetupStepItems([
|
||||||
{
|
{
|
||||||
...setupStepItemsBase[0],
|
...setupStepItemsBase[0],
|
||||||
description: org?.[0]?.name || '',
|
description: org?.[0]?.displayName || '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...setupStepItemsBase[1],
|
...setupStepItemsBase[1],
|
||||||
@ -415,7 +415,7 @@ function OnboardingAddDataSource(): JSX.Element {
|
|||||||
setSetupStepItems([
|
setSetupStepItems([
|
||||||
{
|
{
|
||||||
...setupStepItemsBase[0],
|
...setupStepItemsBase[0],
|
||||||
description: org?.[0]?.name || '',
|
description: org?.[0]?.displayName || '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...setupStepItemsBase[1],
|
...setupStepItemsBase[1],
|
||||||
|
@ -7,27 +7,22 @@ import { useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { requireErrorMessage } from 'utils/form/requireErrorMessage';
|
import { requireErrorMessage } from 'utils/form/requireErrorMessage';
|
||||||
|
|
||||||
function DisplayName({
|
function DisplayName({ index, id: orgId }: DisplayNameProps): JSX.Element {
|
||||||
index,
|
|
||||||
id: orgId,
|
|
||||||
isAnonymous,
|
|
||||||
}: DisplayNameProps): JSX.Element {
|
|
||||||
const [form] = Form.useForm<FormValues>();
|
const [form] = Form.useForm<FormValues>();
|
||||||
const orgName = Form.useWatch('name', form);
|
const orgName = Form.useWatch('displayName', form);
|
||||||
|
|
||||||
const { t } = useTranslation(['organizationsettings', 'common']);
|
const { t } = useTranslation(['organizationsettings', 'common']);
|
||||||
const { org, updateOrg } = useAppContext();
|
const { org, updateOrg } = useAppContext();
|
||||||
const { name } = (org || [])[index];
|
const { displayName } = (org || [])[index];
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
const onSubmit = async (values: FormValues): Promise<void> => {
|
const onSubmit = async (values: FormValues): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const { name } = values;
|
const { displayName } = values;
|
||||||
const { statusCode, error } = await editOrg({
|
const { statusCode, error } = await editOrg({
|
||||||
isAnonymous,
|
displayName,
|
||||||
name,
|
|
||||||
orgId,
|
orgId,
|
||||||
});
|
});
|
||||||
if (statusCode === 200) {
|
if (statusCode === 200) {
|
||||||
@ -36,7 +31,7 @@ function DisplayName({
|
|||||||
ns: 'common',
|
ns: 'common',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
updateOrg(orgId, name);
|
updateOrg(orgId, displayName);
|
||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message:
|
message:
|
||||||
@ -61,18 +56,18 @@ function DisplayName({
|
|||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDisabled = isLoading || orgName === name || !orgName;
|
const isDisabled = isLoading || orgName === displayName || !orgName;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
initialValues={{ name }}
|
initialValues={{ displayName }}
|
||||||
form={form}
|
form={form}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
onFinish={onSubmit}
|
onFinish={onSubmit}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
>
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="name"
|
name="displayName"
|
||||||
label="Display name"
|
label="Display name"
|
||||||
rules={[{ required: true, message: requireErrorMessage('Display name') }]}
|
rules={[{ required: true, message: requireErrorMessage('Display name') }]}
|
||||||
>
|
>
|
||||||
@ -95,11 +90,10 @@ function DisplayName({
|
|||||||
interface DisplayNameProps {
|
interface DisplayNameProps {
|
||||||
index: number;
|
index: number;
|
||||||
id: IUser['id'];
|
id: IUser['id'];
|
||||||
isAnonymous: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormValues {
|
interface FormValues {
|
||||||
name: string;
|
displayName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DisplayName;
|
export default DisplayName;
|
||||||
|
@ -23,12 +23,7 @@ function OrganizationSettings(): JSX.Element {
|
|||||||
<>
|
<>
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
{org.map((e, index) => (
|
{org.map((e, index) => (
|
||||||
<DisplayName
|
<DisplayName key={e.id} id={e.id} index={index} />
|
||||||
isAnonymous={e.isAnonymous}
|
|
||||||
key={e.id}
|
|
||||||
id={e.id}
|
|
||||||
index={index}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button, Form, Input, Space, Switch, Typography } from 'antd';
|
import { Button, Form, Input, Typography } from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import getInviteDetails from 'api/user/getInviteDetails';
|
import getInviteDetails from 'api/user/getInviteDetails';
|
||||||
import loginApi from 'api/user/login';
|
import loginApi from 'api/user/login';
|
||||||
@ -14,13 +14,7 @@ import { useQuery } from 'react-query';
|
|||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { PayloadProps as LoginPrecheckPayloadProps } from 'types/api/user/loginPrecheck';
|
import { PayloadProps as LoginPrecheckPayloadProps } from 'types/api/user/loginPrecheck';
|
||||||
|
|
||||||
import {
|
import { ButtonContainer, FormContainer, FormWrapper, Label } from './styles';
|
||||||
ButtonContainer,
|
|
||||||
FormContainer,
|
|
||||||
FormWrapper,
|
|
||||||
Label,
|
|
||||||
MarginTop,
|
|
||||||
} from './styles';
|
|
||||||
import { isPasswordNotValidMessage, isPasswordValid } from './utils';
|
import { isPasswordNotValidMessage, isPasswordValid } from './utils';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
@ -111,24 +105,15 @@ function SignUp({ version }: SignUpProps): JSX.Element {
|
|||||||
|
|
||||||
const isPreferenceVisible = token === null;
|
const isPreferenceVisible = token === null;
|
||||||
|
|
||||||
const commonHandler = async (
|
const commonHandler = async (values: FormValues): Promise<void> => {
|
||||||
values: FormValues,
|
|
||||||
isPreferenceVisible: boolean,
|
|
||||||
): Promise<void> => {
|
|
||||||
try {
|
try {
|
||||||
const { organizationName, password, firstName, email } = values;
|
const { organizationName, password, firstName, email } = values;
|
||||||
const response = await signUpApi({
|
const response = await signUpApi({
|
||||||
email,
|
email,
|
||||||
name: firstName,
|
name: firstName,
|
||||||
orgName: organizationName,
|
orgDisplayName: organizationName,
|
||||||
password,
|
password,
|
||||||
token: params.get('token') || undefined,
|
token: params.get('token') || undefined,
|
||||||
...(isPreferenceVisible
|
|
||||||
? {
|
|
||||||
isAnonymous: values.isAnonymous,
|
|
||||||
hasOptedUpdates: values.hasOptedUpdates,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
@ -171,7 +156,7 @@ function SignUp({ version }: SignUpProps): JSX.Element {
|
|||||||
const response = await signUpApi({
|
const response = await signUpApi({
|
||||||
email: values.email,
|
email: values.email,
|
||||||
name: values.firstName,
|
name: values.firstName,
|
||||||
orgName: values.organizationName,
|
orgDisplayName: values.organizationName,
|
||||||
password: values.password,
|
password: values.password,
|
||||||
token: params.get('token') || undefined,
|
token: params.get('token') || undefined,
|
||||||
sourceUrl: encodeURIComponent(window.location.href),
|
sourceUrl: encodeURIComponent(window.location.href),
|
||||||
@ -221,14 +206,14 @@ function SignUp({ version }: SignUpProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isPreferenceVisible) {
|
if (isPreferenceVisible) {
|
||||||
await commonHandler(values, true);
|
await commonHandler(values);
|
||||||
} else {
|
} else {
|
||||||
logEvent('Account Created Successfully', {
|
logEvent('Account Created Successfully', {
|
||||||
email: values.email,
|
email: values.email,
|
||||||
name: values.firstName,
|
name: values.firstName,
|
||||||
});
|
});
|
||||||
|
|
||||||
await commonHandler(values, false);
|
await commonHandler(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@ -278,7 +263,6 @@ function SignUp({ version }: SignUpProps): JSX.Element {
|
|||||||
<FormContainer
|
<FormContainer
|
||||||
onFinish={!precheck.sso ? handleSubmit : handleSubmitSSO}
|
onFinish={!precheck.sso ? handleSubmit : handleSubmitSSO}
|
||||||
onValuesChange={handleValuesChange}
|
onValuesChange={handleValuesChange}
|
||||||
initialValues={{ hasOptedUpdates: true, isAnonymous: false }}
|
|
||||||
form={form}
|
form={form}
|
||||||
>
|
>
|
||||||
<Title level={4}>Create your account</Title>
|
<Title level={4}>Create your account</Title>
|
||||||
@ -359,34 +343,6 @@ function SignUp({ version }: SignUpProps): JSX.Element {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isPreferenceVisible && (
|
|
||||||
<>
|
|
||||||
<MarginTop marginTop="2.4375rem">
|
|
||||||
<Space>
|
|
||||||
<FormContainer.Item
|
|
||||||
noStyle
|
|
||||||
name="hasOptedUpdates"
|
|
||||||
valuePropName="checked"
|
|
||||||
>
|
|
||||||
<Switch />
|
|
||||||
</FormContainer.Item>
|
|
||||||
|
|
||||||
<Typography>{t('prompt_keepme_posted')} </Typography>
|
|
||||||
</Space>
|
|
||||||
</MarginTop>
|
|
||||||
|
|
||||||
<MarginTop marginTop="0.5rem">
|
|
||||||
<Space>
|
|
||||||
<FormContainer.Item noStyle name="isAnonymous" valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</FormContainer.Item>
|
|
||||||
<Typography>{t('prompt_anonymise')}</Typography>
|
|
||||||
</Space>
|
|
||||||
</MarginTop>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isPreferenceVisible && (
|
{isPreferenceVisible && (
|
||||||
<Typography.Paragraph
|
<Typography.Paragraph
|
||||||
italic
|
italic
|
||||||
|
@ -82,10 +82,8 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
createdAt: 0,
|
createdAt: 0,
|
||||||
hasOptedUpdates: false,
|
|
||||||
id: userData.payload.orgId,
|
id: userData.payload.orgId,
|
||||||
isAnonymous: false,
|
displayName: userData.payload.organization,
|
||||||
name: userData.payload.organization,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -95,10 +93,8 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
|||||||
...prev.slice(0, orgIndex),
|
...prev.slice(0, orgIndex),
|
||||||
{
|
{
|
||||||
createdAt: 0,
|
createdAt: 0,
|
||||||
hasOptedUpdates: false,
|
|
||||||
id: userData.payload.orgId,
|
id: userData.payload.orgId,
|
||||||
isAnonymous: false,
|
displayName: userData.payload.organization,
|
||||||
name: userData.payload.organization,
|
|
||||||
},
|
},
|
||||||
...prev.slice(orgIndex + 1, prev.length),
|
...prev.slice(orgIndex + 1, prev.length),
|
||||||
];
|
];
|
||||||
@ -209,10 +205,8 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
|||||||
...org.slice(0, orgIndex),
|
...org.slice(0, orgIndex),
|
||||||
{
|
{
|
||||||
createdAt: 0,
|
createdAt: 0,
|
||||||
hasOptedUpdates: false,
|
|
||||||
id: orgId,
|
id: orgId,
|
||||||
isAnonymous: false,
|
displayName: updatedOrgName,
|
||||||
name: updatedOrgName,
|
|
||||||
},
|
},
|
||||||
...org.slice(orgIndex + 1, org.length),
|
...org.slice(orgIndex + 1, org.length),
|
||||||
];
|
];
|
||||||
|
@ -156,10 +156,8 @@ export function getAppContextMock(
|
|||||||
org: [
|
org: [
|
||||||
{
|
{
|
||||||
createdAt: 0,
|
createdAt: 0,
|
||||||
hasOptedUpdates: false,
|
|
||||||
id: 'does-not-matter-id',
|
id: 'does-not-matter-id',
|
||||||
isAnonymous: false,
|
displayName: 'Pentagon',
|
||||||
name: 'Pentagon',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
isFetchingUser: false,
|
isFetchingUser: false,
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
name: string;
|
displayName: string;
|
||||||
isAnonymous: boolean;
|
|
||||||
orgId: string;
|
orgId: string;
|
||||||
hasOptedUpdates?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PayloadProps {
|
export interface PayloadProps {
|
||||||
|
@ -14,6 +14,6 @@ export interface PayloadProps {
|
|||||||
name: User['name'];
|
name: User['name'];
|
||||||
role: ROLES;
|
role: ROLES;
|
||||||
token: string;
|
token: string;
|
||||||
organization: Organization['name'];
|
organization: Organization['displayName'];
|
||||||
precheck?: LoginPrecheckPayloadProps;
|
precheck?: LoginPrecheckPayloadProps;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
export interface Organization {
|
export interface Organization {
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
hasOptedUpdates: boolean;
|
|
||||||
id: string;
|
id: string;
|
||||||
isAnonymous: boolean;
|
displayName: string;
|
||||||
name: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PayloadProps = Organization[];
|
export type PayloadProps = Organization[];
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
name: string;
|
name: string;
|
||||||
orgName: string;
|
orgDisplayName: string;
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
sourceUrl?: string;
|
sourceUrl?: string;
|
||||||
isAnonymous?: boolean;
|
|
||||||
hasOptedUpdates?: boolean;
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ export type Created = 201;
|
|||||||
|
|
||||||
export type Success = 200;
|
export type Success = 200;
|
||||||
|
|
||||||
|
export type SuccessNoContent = 204;
|
||||||
|
|
||||||
export type Forbidden = 403;
|
export type Forbidden = 403;
|
||||||
|
|
||||||
export type BadRequest = 400;
|
export type BadRequest = 400;
|
||||||
@ -14,7 +16,7 @@ export type Conflict = 409;
|
|||||||
|
|
||||||
export type ServerError = 500;
|
export type ServerError = 500;
|
||||||
|
|
||||||
export type SuccessStatusCode = Created | Success;
|
export type SuccessStatusCode = Created | Success | SuccessNoContent;
|
||||||
|
|
||||||
export type ErrorStatusCode =
|
export type ErrorStatusCode =
|
||||||
| Forbidden
|
| Forbidden
|
||||||
|
2
frontend/tests/fixtures/common.ts
vendored
2
frontend/tests/fixtures/common.ts
vendored
@ -33,7 +33,7 @@ export const loginApi = async (page: Page): Promise<void> => {
|
|||||||
body: JSON.stringify(loginApiResponse),
|
body: JSON.stringify(loginApiResponse),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
page.route(`**/org/${userLoginResponse.orgId}`, (route) =>
|
page.route(`**/orgs/me`, (route) =>
|
||||||
route.fulfill({
|
route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
body: JSON.stringify(updateOrgResponse),
|
body: JSON.stringify(updateOrgResponse),
|
||||||
|
80
pkg/modules/organization/implorganization/api.go
Normal file
80
pkg/modules/organization/implorganization/api.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package implorganization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/http/render"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type organizationAPI struct {
|
||||||
|
module organization.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(module organization.Module) organization.API {
|
||||||
|
return &organizationAPI{module: module}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *organizationAPI) Get(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
render.Error(rw, errors.Newf(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "unauthenticated"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid org id"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
organization, err := api.module.Get(r.Context(), orgID)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusOK, organization)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *organizationAPI) GetAll(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
organizations, err := api.module.GetAll(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusOK, organizations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *organizationAPI) Update(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
render.Error(rw, errors.Newf(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "unauthenticated"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
orgID, err := valuer.NewUUID(claims.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid org id"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req *types.Organization
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.ID = orgID
|
||||||
|
err = api.module.Update(r.Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusNoContent, nil)
|
||||||
|
}
|
33
pkg/modules/organization/implorganization/module.go
Normal file
33
pkg/modules/organization/implorganization/module.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package implorganization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type organizationModule struct {
|
||||||
|
store types.OrganizationStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewModule(organizationStore types.OrganizationStore) organization.Module {
|
||||||
|
return &organizationModule{store: organizationStore}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organizationModule) Create(ctx context.Context, organization *types.Organization) error {
|
||||||
|
return o.store.Create(ctx, organization)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organizationModule) Get(ctx context.Context, id valuer.UUID) (*types.Organization, error) {
|
||||||
|
return o.store.Get(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organizationModule) GetAll(ctx context.Context) ([]*types.Organization, error) {
|
||||||
|
return o.store.GetAll(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organizationModule) Update(ctx context.Context, updatedOrganization *types.Organization) error {
|
||||||
|
return o.store.Update(ctx, updatedOrganization)
|
||||||
|
}
|
102
pkg/modules/organization/implorganization/store.go
Normal file
102
pkg/modules/organization/implorganization/store.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package implorganization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type store struct {
|
||||||
|
store sqlstore.SQLStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStore(db sqlstore.SQLStore) types.OrganizationStore {
|
||||||
|
return &store{store: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *store) Create(ctx context.Context, organization *types.Organization) error {
|
||||||
|
_, err := s.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewInsert().
|
||||||
|
Model(organization).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to create organization")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *store) Get(ctx context.Context, id valuer.UUID) (*types.Organization, error) {
|
||||||
|
organization := new(types.Organization)
|
||||||
|
err := s.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewSelect().
|
||||||
|
Model(organization).
|
||||||
|
Where("id = ?", id.StringValue()).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "no organization found with id: %s", id.StringValue())
|
||||||
|
}
|
||||||
|
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to get organization with id: %s", id.StringValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
return organization, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *store) GetAll(ctx context.Context) ([]*types.Organization, error) {
|
||||||
|
organizations := make([]*types.Organization, 0)
|
||||||
|
err := s.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewSelect().
|
||||||
|
Model(&organizations).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "no organizations found")
|
||||||
|
}
|
||||||
|
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to get all organizations")
|
||||||
|
}
|
||||||
|
|
||||||
|
return organizations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *store) Update(ctx context.Context, organization *types.Organization) error {
|
||||||
|
_, err := s.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewUpdate().
|
||||||
|
Model(organization).
|
||||||
|
Set("display_name = ?", organization.DisplayName).
|
||||||
|
Set("updated_at = ?", time.Now()).
|
||||||
|
Where("id = ?", organization.ID.StringValue()).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to update organization with id: %s", organization.ID.StringValue())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *store) Delete(ctx context.Context, id valuer.UUID) error {
|
||||||
|
_, err := s.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewDelete().
|
||||||
|
Model(new(types.Organization)).
|
||||||
|
Where("id = ?", id.StringValue()).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to delete organization with id: %s", id.StringValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
34
pkg/modules/organization/organization.go
Normal file
34
pkg/modules/organization/organization.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package organization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Module interface {
|
||||||
|
// Create creates the given organization
|
||||||
|
Create(context.Context, *types.Organization) error
|
||||||
|
|
||||||
|
// Get gets the organization based on the given id
|
||||||
|
Get(context.Context, valuer.UUID) (*types.Organization, error)
|
||||||
|
|
||||||
|
// GetAll gets all the organizations
|
||||||
|
GetAll(context.Context) ([]*types.Organization, error)
|
||||||
|
|
||||||
|
// Update updates the given organization based on id present
|
||||||
|
Update(context.Context, *types.Organization) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
// Get gets the organization based on the id in claims
|
||||||
|
Get(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
// GetAll gets all the organizations
|
||||||
|
GetAll(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
// Update updates the organization based on the id in claims
|
||||||
|
Update(http.ResponseWriter, *http.Request)
|
||||||
|
}
|
@ -15,7 +15,6 @@ type API interface {
|
|||||||
GetOrgPreference(http.ResponseWriter, *http.Request)
|
GetOrgPreference(http.ResponseWriter, *http.Request)
|
||||||
UpdateOrgPreference(http.ResponseWriter, *http.Request)
|
UpdateOrgPreference(http.ResponseWriter, *http.Request)
|
||||||
GetAllOrgPreferences(http.ResponseWriter, *http.Request)
|
GetAllOrgPreferences(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
GetUserPreference(http.ResponseWriter, *http.Request)
|
GetUserPreference(http.ResponseWriter, *http.Request)
|
||||||
UpdateUserPreference(http.ResponseWriter, *http.Request)
|
UpdateUserPreference(http.ResponseWriter, *http.Request)
|
||||||
GetAllUserPreferences(http.ResponseWriter, *http.Request)
|
GetAllUserPreferences(http.ResponseWriter, *http.Request)
|
||||||
|
@ -10,7 +10,6 @@ type Usecase interface {
|
|||||||
GetOrgPreference(ctx context.Context, preferenceId string, orgId string) (*preferencetypes.GettablePreference, error)
|
GetOrgPreference(ctx context.Context, preferenceId string, orgId string) (*preferencetypes.GettablePreference, error)
|
||||||
UpdateOrgPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, orgId string) error
|
UpdateOrgPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, orgId string) error
|
||||||
GetAllOrgPreferences(ctx context.Context, orgId string) ([]*preferencetypes.PreferenceWithValue, error)
|
GetAllOrgPreferences(ctx context.Context, orgId string) ([]*preferencetypes.PreferenceWithValue, error)
|
||||||
|
|
||||||
GetUserPreference(ctx context.Context, preferenceId string, orgId string, userId string) (*preferencetypes.GettablePreference, error)
|
GetUserPreference(ctx context.Context, preferenceId string, orgId string, userId string) (*preferencetypes.GettablePreference, error)
|
||||||
UpdateUserPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, userId string) error
|
UpdateUserPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, userId string) error
|
||||||
GetAllUserPreferences(ctx context.Context, orgId string, userId string) ([]*preferencetypes.PreferenceWithValue, error)
|
GetAllUserPreferences(ctx context.Context, orgId string, userId string) ([]*preferencetypes.PreferenceWithValue, error)
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/auth"
|
"github.com/SigNoz/signoz/pkg/query-service/auth"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/dao"
|
"github.com/SigNoz/signoz/pkg/query-service/dao"
|
||||||
@ -20,7 +22,8 @@ func TestRegenerateConnectionUrlWithUpdatedConfig(t *testing.T) {
|
|||||||
controller, err := NewController(sqlStore)
|
controller, err := NewController(sqlStore)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
user, apiErr := createTestUser()
|
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore))
|
||||||
|
user, apiErr := createTestUser(organizationModule)
|
||||||
require.Nil(apiErr)
|
require.Nil(apiErr)
|
||||||
|
|
||||||
// should be able to generate connection url for
|
// should be able to generate connection url for
|
||||||
@ -66,8 +69,8 @@ func TestAgentCheckIns(t *testing.T) {
|
|||||||
sqlStore := utils.NewQueryServiceDBForTests(t)
|
sqlStore := utils.NewQueryServiceDBForTests(t)
|
||||||
controller, err := NewController(sqlStore)
|
controller, err := NewController(sqlStore)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore))
|
||||||
user, apiErr := createTestUser()
|
user, apiErr := createTestUser(organizationModule)
|
||||||
require.Nil(apiErr)
|
require.Nil(apiErr)
|
||||||
|
|
||||||
// An agent should be able to check in from a cloud account even
|
// An agent should be able to check in from a cloud account even
|
||||||
@ -118,7 +121,7 @@ func TestAgentCheckIns(t *testing.T) {
|
|||||||
|
|
||||||
// After disconnecting existing account record, the agent should be able to
|
// After disconnecting existing account record, the agent should be able to
|
||||||
// connected for a particular cloud account id
|
// connected for a particular cloud account id
|
||||||
_, apiErr = controller.DisconnectAccount(
|
_, _ = controller.DisconnectAccount(
|
||||||
context.TODO(), user.OrgID, "aws", testAccountId1,
|
context.TODO(), user.OrgID, "aws", testAccountId1,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -153,7 +156,8 @@ func TestCantDisconnectNonExistentAccount(t *testing.T) {
|
|||||||
controller, err := NewController(sqlStore)
|
controller, err := NewController(sqlStore)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
user, apiErr := createTestUser()
|
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore))
|
||||||
|
user, apiErr := createTestUser(organizationModule)
|
||||||
require.Nil(apiErr)
|
require.Nil(apiErr)
|
||||||
|
|
||||||
// Attempting to disconnect a non-existent account should return error
|
// Attempting to disconnect a non-existent account should return error
|
||||||
@ -171,7 +175,8 @@ func TestConfigureService(t *testing.T) {
|
|||||||
controller, err := NewController(sqlStore)
|
controller, err := NewController(sqlStore)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
user, apiErr := createTestUser()
|
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore))
|
||||||
|
user, apiErr := createTestUser(organizationModule)
|
||||||
require.Nil(apiErr)
|
require.Nil(apiErr)
|
||||||
|
|
||||||
// create a connected account
|
// create a connected account
|
||||||
@ -286,19 +291,18 @@ func makeTestConnectedAccount(t *testing.T, orgId string, controller *Controller
|
|||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestUser() (*types.User, *model.ApiError) {
|
func createTestUser(organizationModule organization.Module) (*types.User, *model.ApiError) {
|
||||||
// Create a test user for auth
|
// Create a test user for auth
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
org, apiErr := dao.DB().CreateOrg(ctx, &types.Organization{
|
organization := types.NewOrganization("test")
|
||||||
Name: "test",
|
err := organizationModule.Create(ctx, organization)
|
||||||
})
|
if err != nil {
|
||||||
if apiErr != nil {
|
return nil, model.InternalError(err)
|
||||||
return nil, apiErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group, apiErr := dao.DB().GetGroupByName(ctx, constants.AdminGroup)
|
group, apiErr := dao.DB().GetGroupByName(ctx, constants.AdminGroup)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
return nil, apiErr
|
return nil, model.InternalError(apiErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.InitAuthCache(ctx)
|
auth.InitAuthCache(ctx)
|
||||||
@ -311,7 +315,7 @@ func createTestUser() (*types.User, *model.ApiError) {
|
|||||||
Name: "test",
|
Name: "test",
|
||||||
Email: userId[:8] + "test@test.com",
|
Email: userId[:8] + "test@test.com",
|
||||||
Password: "test",
|
Password: "test",
|
||||||
OrgID: org.ID,
|
OrgID: organization.ID.StringValue(),
|
||||||
GroupID: group.ID,
|
GroupID: group.ID,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/apis/fields"
|
"github.com/SigNoz/signoz/pkg/apis/fields"
|
||||||
errorsV2 "github.com/SigNoz/signoz/pkg/errors"
|
errorsV2 "github.com/SigNoz/signoz/pkg/errors"
|
||||||
"github.com/SigNoz/signoz/pkg/http/render"
|
"github.com/SigNoz/signoz/pkg/http/render"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/preference"
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
|
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/metricsexplorer"
|
"github.com/SigNoz/signoz/pkg/query-service/app/metricsexplorer"
|
||||||
@ -148,6 +149,9 @@ type APIHandler struct {
|
|||||||
Signoz *signoz.SigNoz
|
Signoz *signoz.SigNoz
|
||||||
|
|
||||||
Preference preference.API
|
Preference preference.API
|
||||||
|
|
||||||
|
OrganizationAPI organization.API
|
||||||
|
OrganizationModule organization.Module
|
||||||
}
|
}
|
||||||
|
|
||||||
type APIHandlerOpts struct {
|
type APIHandlerOpts struct {
|
||||||
@ -197,6 +201,8 @@ type APIHandlerOpts struct {
|
|||||||
Signoz *signoz.SigNoz
|
Signoz *signoz.SigNoz
|
||||||
|
|
||||||
Preference preference.API
|
Preference preference.API
|
||||||
|
OrganizationAPI organization.API
|
||||||
|
OrganizationModule organization.Module
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAPIHandler returns an APIHandler
|
// NewAPIHandler returns an APIHandler
|
||||||
@ -267,6 +273,8 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) {
|
|||||||
Signoz: opts.Signoz,
|
Signoz: opts.Signoz,
|
||||||
Preference: opts.Preference,
|
Preference: opts.Preference,
|
||||||
FieldsAPI: opts.FieldsAPI,
|
FieldsAPI: opts.FieldsAPI,
|
||||||
|
OrganizationAPI: opts.OrganizationAPI,
|
||||||
|
OrganizationModule: opts.OrganizationModule,
|
||||||
}
|
}
|
||||||
|
|
||||||
logsQueryBuilder := logsv3.PrepareLogsQuery
|
logsQueryBuilder := logsv3.PrepareLogsQuery
|
||||||
@ -623,11 +631,12 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) {
|
|||||||
router.HandleFunc("/api/v1/rbac/role/{id}", am.SelfAccess(aH.getRole)).Methods(http.MethodGet)
|
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)
|
router.HandleFunc("/api/v1/rbac/role/{id}", am.AdminAccess(aH.editRole)).Methods(http.MethodPut)
|
||||||
|
|
||||||
router.HandleFunc("/api/v1/org", am.AdminAccess(aH.getOrgs)).Methods(http.MethodGet)
|
|
||||||
router.HandleFunc("/api/v1/org/{id}", am.AdminAccess(aH.getOrg)).Methods(http.MethodGet)
|
|
||||||
router.HandleFunc("/api/v1/org/{id}", am.AdminAccess(aH.editOrg)).Methods(http.MethodPut)
|
|
||||||
router.HandleFunc("/api/v1/orgUsers/{id}", am.AdminAccess(aH.getOrgUsers)).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/orgUsers/{id}", am.AdminAccess(aH.getOrgUsers)).Methods(http.MethodGet)
|
||||||
|
|
||||||
|
router.HandleFunc("/api/v2/orgs", am.AdminAccess(aH.getOrgs)).Methods(http.MethodGet)
|
||||||
|
router.HandleFunc("/api/v2/orgs/me", am.AdminAccess(aH.getOrg)).Methods(http.MethodGet)
|
||||||
|
router.HandleFunc("/api/v2/orgs/me", am.AdminAccess(aH.updateOrg)).Methods(http.MethodPut)
|
||||||
|
|
||||||
router.HandleFunc("/api/v1/getResetPasswordToken/{id}", am.AdminAccess(aH.getResetPasswordToken)).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/getResetPasswordToken/{id}", am.AdminAccess(aH.getResetPasswordToken)).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/api/v1/resetPassword", am.OpenAccess(aH.resetPassword)).Methods(http.MethodPost)
|
router.HandleFunc("/api/v1/resetPassword", am.OpenAccess(aH.resetPassword)).Methods(http.MethodPost)
|
||||||
router.HandleFunc("/api/v1/changePassword/{id}", am.SelfAccess(aH.changePassword)).Methods(http.MethodPost)
|
router.HandleFunc("/api/v1/changePassword/{id}", am.SelfAccess(aH.changePassword)).Methods(http.MethodPost)
|
||||||
@ -2058,7 +2067,7 @@ func (aH *APIHandler) inviteUsers(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (aH *APIHandler) getInvite(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getInvite(w http.ResponseWriter, r *http.Request) {
|
||||||
token := mux.Vars(r)["token"]
|
token := mux.Vars(r)["token"]
|
||||||
|
|
||||||
resp, err := auth.GetInvite(context.Background(), token)
|
resp, err := auth.GetInvite(context.Background(), token, aH.OrganizationModule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorNotFound}, nil)
|
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorNotFound}, nil)
|
||||||
return
|
return
|
||||||
@ -2096,10 +2105,13 @@ func (aH *APIHandler) listPendingInvites(w http.ResponseWriter, r *http.Request)
|
|||||||
// we should include org name field in the invite table, or do a join query.
|
// we should include org name field in the invite table, or do a join query.
|
||||||
var resp []*model.InvitationResponseObject
|
var resp []*model.InvitationResponseObject
|
||||||
for _, inv := range invites {
|
for _, inv := range invites {
|
||||||
|
orgID, err := valuer.NewUUID(inv.OrgID)
|
||||||
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgID)
|
if err != nil {
|
||||||
if apiErr != nil {
|
render.Error(w, errorsV2.Newf(errorsV2.TypeInvalidInput, errorsV2.CodeInvalidInput, "invalid org_id in the invite"))
|
||||||
RespondError(w, apiErr, nil)
|
}
|
||||||
|
org, err := aH.OrganizationModule.Get(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(w, errorsV2.Newf(errorsV2.TypeInternal, errorsV2.CodeInternal, err.Error()))
|
||||||
}
|
}
|
||||||
resp = append(resp, &model.InvitationResponseObject{
|
resp = append(resp, &model.InvitationResponseObject{
|
||||||
Name: inv.Name,
|
Name: inv.Name,
|
||||||
@ -2124,7 +2136,7 @@ func (aH *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, apiErr := auth.Register(context.Background(), req, aH.Signoz.Alertmanager)
|
_, apiErr := auth.Register(context.Background(), req, aH.Signoz.Alertmanager, aH.OrganizationModule)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, nil)
|
RespondError(w, apiErr, nil)
|
||||||
return
|
return
|
||||||
@ -2398,49 +2410,15 @@ func (aH *APIHandler) editRole(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) getOrgs(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getOrgs(w http.ResponseWriter, r *http.Request) {
|
||||||
orgs, apiErr := dao.DB().GetOrgs(context.Background())
|
aH.OrganizationAPI.GetAll(w, r)
|
||||||
if apiErr != nil {
|
|
||||||
RespondError(w, apiErr, "Failed to fetch orgs from the DB")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
aH.WriteJSON(w, r, orgs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) getOrg(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getOrg(w http.ResponseWriter, r *http.Request) {
|
||||||
id := mux.Vars(r)["id"]
|
aH.OrganizationAPI.Get(w, r)
|
||||||
org, apiErr := dao.DB().GetOrg(context.Background(), id)
|
|
||||||
if apiErr != nil {
|
|
||||||
RespondError(w, apiErr, "Failed to fetch org from the DB")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
aH.WriteJSON(w, r, org)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) editOrg(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) updateOrg(w http.ResponseWriter, r *http.Request) {
|
||||||
id := mux.Vars(r)["id"]
|
aH.OrganizationAPI.Update(w, r)
|
||||||
req, err := parseEditOrgRequest(r)
|
|
||||||
if aH.HandleError(w, err, http.StatusBadRequest) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.ID = id
|
|
||||||
if apiErr := dao.DB().EditOrg(context.Background(), req); apiErr != nil {
|
|
||||||
RespondError(w, apiErr, "Failed to update org in the DB")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data := map[string]interface{}{
|
|
||||||
"hasOptedUpdates": req.HasOptedUpdates,
|
|
||||||
"isAnonymous": req.IsAnonymous,
|
|
||||||
"organizationName": req.Name,
|
|
||||||
}
|
|
||||||
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
|
||||||
if !ok {
|
|
||||||
zap.L().Error("failed to get user email from jwt")
|
|
||||||
}
|
|
||||||
telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_ORG_SETTINGS, data, claims.Email, true, false)
|
|
||||||
|
|
||||||
aH.WriteJSON(w, r, map[string]string{"data": "org updated successfully"})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) getOrgUsers(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getOrgUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -11,10 +12,11 @@ import (
|
|||||||
func TestIntegrationLifecycle(t *testing.T) {
|
func TestIntegrationLifecycle(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
mgr := NewTestIntegrationsManager(t)
|
mgr, store := NewTestIntegrationsManager(t)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
user, apiErr := createTestUser()
|
organizationModule := implorganization.NewModule(implorganization.NewStore(store))
|
||||||
|
user, apiErr := createTestUser(organizationModule)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
t.Fatalf("could not create test user: %v", apiErr)
|
t.Fatalf("could not create test user: %v", apiErr)
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,21 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/auth"
|
"github.com/SigNoz/signoz/pkg/query-service/auth"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/dao"
|
"github.com/SigNoz/signoz/pkg/query-service/dao"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/utils"
|
"github.com/SigNoz/signoz/pkg/query-service/utils"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
"github.com/SigNoz/signoz/pkg/types"
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
"github.com/SigNoz/signoz/pkg/types/pipelinetypes"
|
"github.com/SigNoz/signoz/pkg/types/pipelinetypes"
|
||||||
ruletypes "github.com/SigNoz/signoz/pkg/types/ruletypes"
|
ruletypes "github.com/SigNoz/signoz/pkg/types/ruletypes"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewTestIntegrationsManager(t *testing.T) *Manager {
|
func NewTestIntegrationsManager(t *testing.T) (*Manager, sqlstore.SQLStore) {
|
||||||
testDB := utils.NewQueryServiceDBForTests(t)
|
testDB := utils.NewQueryServiceDBForTests(t)
|
||||||
|
|
||||||
installedIntegrationsRepo, err := NewInstalledIntegrationsSqliteRepo(testDB)
|
installedIntegrationsRepo, err := NewInstalledIntegrationsSqliteRepo(testDB)
|
||||||
@ -28,22 +30,21 @@ func NewTestIntegrationsManager(t *testing.T) *Manager {
|
|||||||
return &Manager{
|
return &Manager{
|
||||||
availableIntegrationsRepo: &TestAvailableIntegrationsRepo{},
|
availableIntegrationsRepo: &TestAvailableIntegrationsRepo{},
|
||||||
installedIntegrationsRepo: installedIntegrationsRepo,
|
installedIntegrationsRepo: installedIntegrationsRepo,
|
||||||
}
|
}, testDB
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestUser() (*types.User, *model.ApiError) {
|
func createTestUser(organizationModule organization.Module) (*types.User, *model.ApiError) {
|
||||||
// Create a test user for auth
|
// Create a test user for auth
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
org, apiErr := dao.DB().CreateOrg(ctx, &types.Organization{
|
organization := types.NewOrganization("test")
|
||||||
Name: "test",
|
err := organizationModule.Create(ctx, organization)
|
||||||
})
|
if err != nil {
|
||||||
if apiErr != nil {
|
return nil, model.InternalError(err)
|
||||||
return nil, apiErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group, apiErr := dao.DB().GetGroupByName(ctx, constants.AdminGroup)
|
group, apiErr := dao.DB().GetGroupByName(ctx, constants.AdminGroup)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
return nil, apiErr
|
return nil, model.InternalError(apiErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.InitAuthCache(ctx)
|
auth.InitAuthCache(ctx)
|
||||||
@ -56,7 +57,7 @@ func createTestUser() (*types.User, *model.ApiError) {
|
|||||||
Name: "test",
|
Name: "test",
|
||||||
Email: userId[:8] + "test@test.com",
|
Email: userId[:8] + "test@test.com",
|
||||||
Password: "test",
|
Password: "test",
|
||||||
OrgID: org.ID,
|
OrgID: organization.ID.StringValue(),
|
||||||
GroupID: group.ID,
|
GroupID: group.ID,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
@ -142,7 +142,7 @@ func (r *Repo) GetDefaultOrgID(ctx context.Context) (string, *model.ApiError) {
|
|||||||
if len(orgs) == 0 {
|
if len(orgs) == 0 {
|
||||||
return "", model.InternalError(errors.New("no orgs found"))
|
return "", model.InternalError(errors.New("no orgs found"))
|
||||||
}
|
}
|
||||||
return orgs[0].ID, nil
|
return orgs[0].ID.StringValue(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPipelines returns pipeline and errors (if any)
|
// GetPipelines returns pipeline and errors (if any)
|
||||||
|
@ -536,14 +536,6 @@ func parseSetApdexScoreRequest(r *http.Request) (*types.ApdexSettings, error) {
|
|||||||
return &req, nil
|
return &req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseInsertIngestionKeyRequest(r *http.Request) (*model.IngestionKey, error) {
|
|
||||||
var req model.IngestionKey
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseRegisterRequest(r *http.Request) (*auth.RegisterRequest, error) {
|
func parseRegisterRequest(r *http.Request) (*auth.RegisterRequest, error) {
|
||||||
var req auth.RegisterRequest
|
var req auth.RegisterRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||||
"github.com/SigNoz/signoz/pkg/apis/fields"
|
"github.com/SigNoz/signoz/pkg/apis/fields"
|
||||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/preference"
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
preferencecore "github.com/SigNoz/signoz/pkg/modules/preference/core"
|
preferencecore "github.com/SigNoz/signoz/pkg/modules/preference/core"
|
||||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||||
@ -185,7 +186,9 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
telemetry.GetInstance().SetReader(reader)
|
telemetry.GetInstance().SetReader(reader)
|
||||||
preferenceModule := preference.NewAPI(preferencecore.NewPreference(preferencecore.NewStore(serverOptions.SigNoz.SQLStore), preferencetypes.NewDefaultPreferenceMap()))
|
preferenceAPI := preference.NewAPI(preferencecore.NewPreference(preferencecore.NewStore(serverOptions.SigNoz.SQLStore), preferencetypes.NewDefaultPreferenceMap()))
|
||||||
|
organizationAPI := implorganization.NewAPI(implorganization.NewModule(implorganization.NewStore(serverOptions.SigNoz.SQLStore)))
|
||||||
|
organizationModule := implorganization.NewModule(implorganization.NewStore(serverOptions.SigNoz.SQLStore))
|
||||||
apiHandler, err := NewAPIHandler(APIHandlerOpts{
|
apiHandler, err := NewAPIHandler(APIHandlerOpts{
|
||||||
Reader: reader,
|
Reader: reader,
|
||||||
SkipConfig: skipConfig,
|
SkipConfig: skipConfig,
|
||||||
@ -204,7 +207,9 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
AlertmanagerAPI: alertmanager.NewAPI(serverOptions.SigNoz.Alertmanager),
|
AlertmanagerAPI: alertmanager.NewAPI(serverOptions.SigNoz.Alertmanager),
|
||||||
FieldsAPI: fields.NewAPI(serverOptions.SigNoz.TelemetryStore),
|
FieldsAPI: fields.NewAPI(serverOptions.SigNoz.TelemetryStore),
|
||||||
Signoz: serverOptions.SigNoz,
|
Signoz: serverOptions.SigNoz,
|
||||||
Preference: preferenceModule,
|
Preference: preferenceAPI,
|
||||||
|
OrganizationAPI: organizationAPI,
|
||||||
|
OrganizationModule: organizationModule,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/dao"
|
"github.com/SigNoz/signoz/pkg/query-service/dao"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||||
@ -277,7 +278,7 @@ func RevokeInvite(ctx context.Context, email string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetInvite returns an invitation object for the given token.
|
// GetInvite returns an invitation object for the given token.
|
||||||
func GetInvite(ctx context.Context, token string) (*model.InvitationResponseObject, error) {
|
func GetInvite(ctx context.Context, token string, organizationModule organization.Module) (*model.InvitationResponseObject, error) {
|
||||||
zap.L().Debug("GetInvite method invoked for token", zap.String("token", token))
|
zap.L().Debug("GetInvite method invoked for token", zap.String("token", token))
|
||||||
|
|
||||||
inv, apiErr := dao.DB().GetInviteFromToken(ctx, token)
|
inv, apiErr := dao.DB().GetInviteFromToken(ctx, token)
|
||||||
@ -289,11 +290,13 @@ func GetInvite(ctx context.Context, token string) (*model.InvitationResponseObje
|
|||||||
return nil, errors.New("user is not invited")
|
return nil, errors.New("user is not invited")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Ahsan): This is not the best way to add org name in the invite response. We should
|
orgID, err := valuer.NewUUID(inv.OrgID)
|
||||||
// either include org name in the invite table or do a join query.
|
if err != nil {
|
||||||
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgID)
|
return nil, err
|
||||||
if apiErr != nil {
|
}
|
||||||
return nil, errors.Wrap(apiErr.Err, "failed to query the DB")
|
org, err := organizationModule.Get(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to query the DB")
|
||||||
}
|
}
|
||||||
return &model.InvitationResponseObject{
|
return &model.InvitationResponseObject{
|
||||||
Name: inv.Name,
|
Name: inv.Name,
|
||||||
@ -301,7 +304,7 @@ func GetInvite(ctx context.Context, token string) (*model.InvitationResponseObje
|
|||||||
Token: inv.Token,
|
Token: inv.Token,
|
||||||
CreatedAt: inv.CreatedAt.Unix(),
|
CreatedAt: inv.CreatedAt.Unix(),
|
||||||
Role: inv.Role,
|
Role: inv.Role,
|
||||||
Organization: org.Name,
|
Organization: org.DisplayName,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,19 +395,17 @@ func ChangePassword(ctx context.Context, req *model.ChangePasswordRequest) *mode
|
|||||||
type RegisterRequest struct {
|
type RegisterRequest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
OrgID string `json:"orgId"`
|
OrgID string `json:"orgId"`
|
||||||
|
OrgDisplayName string `json:"orgDisplayName"`
|
||||||
OrgName string `json:"orgName"`
|
OrgName string `json:"orgName"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
InviteToken string `json:"token"`
|
InviteToken string `json:"token"`
|
||||||
IsAnonymous bool `json:"isAnonymous"`
|
|
||||||
HasOptedUpdates bool `json:"hasOptedUpdates"`
|
|
||||||
|
|
||||||
// reference URL to track where the register request is coming from
|
// reference URL to track where the register request is coming from
|
||||||
SourceUrl string `json:"sourceUrl"`
|
SourceUrl string `json:"sourceUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*types.User, *model.ApiError) {
|
func RegisterFirstUser(ctx context.Context, req *RegisterRequest, organizationModule organization.Module) (*types.User, *model.ApiError) {
|
||||||
|
|
||||||
if req.Email == "" {
|
if req.Email == "" {
|
||||||
return nil, model.BadRequest(model.ErrEmailRequired{})
|
return nil, model.BadRequest(model.ErrEmailRequired{})
|
||||||
}
|
}
|
||||||
@ -414,13 +415,10 @@ func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*types.User,
|
|||||||
}
|
}
|
||||||
|
|
||||||
groupName := constants.AdminGroup
|
groupName := constants.AdminGroup
|
||||||
|
organization := types.NewOrganization(req.OrgDisplayName)
|
||||||
// modify this to use bun
|
err := organizationModule.Create(ctx, organization)
|
||||||
org, apierr := dao.DB().CreateOrg(ctx,
|
if err != nil {
|
||||||
&types.Organization{Name: req.OrgName, IsAnonymous: req.IsAnonymous, HasOptedUpdates: req.HasOptedUpdates})
|
return nil, model.InternalError(err)
|
||||||
if apierr != nil {
|
|
||||||
zap.L().Error("CreateOrg failed", zap.Error(apierr.ToError()))
|
|
||||||
return nil, apierr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group, apiErr := dao.DB().GetGroupByName(ctx, groupName)
|
group, apiErr := dao.DB().GetGroupByName(ctx, groupName)
|
||||||
@ -430,8 +428,6 @@ func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*types.User,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var hash string
|
var hash string
|
||||||
var err error
|
|
||||||
|
|
||||||
hash, err = PasswordHash(req.Password)
|
hash, err = PasswordHash(req.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.L().Error("failed to generate password hash when registering a user", zap.Error(err))
|
zap.L().Error("failed to generate password hash when registering a user", zap.Error(err))
|
||||||
@ -448,7 +444,7 @@ func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*types.User,
|
|||||||
},
|
},
|
||||||
ProfilePictureURL: "", // Currently unused
|
ProfilePictureURL: "", // Currently unused
|
||||||
GroupID: group.ID,
|
GroupID: group.ID,
|
||||||
OrgID: org.ID,
|
OrgID: organization.ID.StringValue(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return dao.DB().CreateUser(ctx, user, true)
|
return dao.DB().CreateUser(ctx, user, true)
|
||||||
@ -553,7 +549,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
|
// 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
|
// and also the first registration is an enforced ADMIN registration. Every subsequent request will
|
||||||
// need an invite token to go through.
|
// need an invite token to go through.
|
||||||
func Register(ctx context.Context, req *RegisterRequest, alertmanager alertmanager.Alertmanager) (*types.User, *model.ApiError) {
|
func Register(ctx context.Context, req *RegisterRequest, alertmanager alertmanager.Alertmanager, organizationModule organization.Module) (*types.User, *model.ApiError) {
|
||||||
users, err := dao.DB().GetUsers(ctx)
|
users, err := dao.DB().GetUsers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.InternalError(fmt.Errorf("failed to get user count"))
|
return nil, model.InternalError(fmt.Errorf("failed to get user count"))
|
||||||
@ -561,7 +557,7 @@ func Register(ctx context.Context, req *RegisterRequest, alertmanager alertmanag
|
|||||||
|
|
||||||
switch len(users) {
|
switch len(users) {
|
||||||
case 0:
|
case 0:
|
||||||
user, err := RegisterFirstUser(ctx, req)
|
user, err := RegisterFirstUser(ctx, req, organizationModule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,6 @@ type Queries interface {
|
|||||||
GetGroupByName(ctx context.Context, name string) (*types.Group, *model.ApiError)
|
GetGroupByName(ctx context.Context, name string) (*types.Group, *model.ApiError)
|
||||||
GetGroups(ctx context.Context) ([]types.Group, *model.ApiError)
|
GetGroups(ctx context.Context) ([]types.Group, *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) (*types.ResetPasswordRequest, *model.ApiError)
|
GetResetPasswordEntry(ctx context.Context, token string) (*types.ResetPasswordRequest, *model.ApiError)
|
||||||
GetUsersByOrg(ctx context.Context, orgId string) ([]types.GettableUser, *model.ApiError)
|
GetUsersByOrg(ctx context.Context, orgId string) ([]types.GettableUser, *model.ApiError)
|
||||||
GetUsersByGroup(ctx context.Context, groupId string) ([]types.GettableUser, *model.ApiError)
|
GetUsersByGroup(ctx context.Context, groupId string) ([]types.GettableUser, *model.ApiError)
|
||||||
@ -50,10 +46,6 @@ type Mutations interface {
|
|||||||
CreateGroup(ctx context.Context, group *types.Group) (*types.Group, *model.ApiError)
|
CreateGroup(ctx context.Context, group *types.Group) (*types.Group, *model.ApiError)
|
||||||
DeleteGroup(ctx context.Context, id string) *model.ApiError
|
DeleteGroup(ctx context.Context, id string) *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 *types.ResetPasswordRequest) *model.ApiError
|
CreateResetPasswordEntry(ctx context.Context, req *types.ResetPasswordRequest) *model.ApiError
|
||||||
DeleteResetPasswordEntry(ctx context.Context, token string) *model.ApiError
|
DeleteResetPasswordEntry(ctx context.Context, token string) *model.ApiError
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ func (mds *ModelDaoSqlite) initializeOrgPreferences(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set telemetry fields from userPreferences
|
// set telemetry fields from userPreferences
|
||||||
telemetry.GetInstance().SetDistinctId(org.ID)
|
telemetry.GetInstance().SetDistinctId(org.ID.StringValue())
|
||||||
|
|
||||||
users, _ := mds.GetUsers(ctx)
|
users, _ := mds.GetUsers(ctx)
|
||||||
countUsers := len(users)
|
countUsers := len(users)
|
||||||
|
@ -3,7 +3,6 @@ package sqlite
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
|
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
|
||||||
@ -95,71 +94,6 @@ func (mds *ModelDaoSqlite) GetInvites(ctx context.Context, orgID string) ([]type
|
|||||||
return invites, nil
|
return invites, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) CreateOrg(ctx context.Context,
|
|
||||||
org *types.Organization) (*types.Organization, *model.ApiError) {
|
|
||||||
|
|
||||||
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}
|
|
||||||
}
|
|
||||||
return org, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetOrg(ctx context.Context,
|
|
||||||
id string) (*types.Organization, *model.ApiError) {
|
|
||||||
|
|
||||||
orgs := []types.Organization{}
|
|
||||||
|
|
||||||
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,
|
|
||||||
Err: errors.New("Found multiple org with same ID"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(orgs) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return &orgs[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetOrgByName(ctx context.Context,
|
|
||||||
name string) (*types.Organization, *model.ApiError) {
|
|
||||||
|
|
||||||
orgs := []types.Organization{}
|
|
||||||
|
|
||||||
if err := mds.bundb.NewSelect().
|
|
||||||
Model(&orgs).
|
|
||||||
Where("name = ?", name).
|
|
||||||
Scan(ctx); err != nil {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(orgs) > 1 {
|
|
||||||
return nil, &model.ApiError{
|
|
||||||
Typ: model.ErrorInternal,
|
|
||||||
Err: errors.New("Multiple orgs with same ID found"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(orgs) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return &orgs[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]types.Organization, *model.ApiError) {
|
func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]types.Organization, *model.ApiError) {
|
||||||
var orgs []types.Organization
|
var orgs []types.Organization
|
||||||
err := mds.bundb.NewSelect().
|
err := mds.bundb.NewSelect().
|
||||||
@ -172,37 +106,6 @@ func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]types.Organization, *
|
|||||||
return orgs, nil
|
return orgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
telemetry.GetInstance().SetTelemetryAnonymous(org.IsAnonymous)
|
|
||||||
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)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) CreateUser(ctx context.Context,
|
func (mds *ModelDaoSqlite) CreateUser(ctx context.Context,
|
||||||
user *types.User, isFirstUser bool) (*types.User, *model.ApiError) {
|
user *types.User, isFirstUser bool) (*types.User, *model.ApiError) {
|
||||||
_, err := mds.bundb.NewInsert().
|
_, err := mds.bundb.NewInsert().
|
||||||
@ -306,7 +209,7 @@ func (mds *ModelDaoSqlite) GetUser(ctx context.Context,
|
|||||||
Table("users").
|
Table("users").
|
||||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
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("g.name as role").
|
||||||
ColumnExpr("o.name as organization").
|
ColumnExpr("o.display_name as organization").
|
||||||
Join("JOIN groups g ON g.id = users.group_id").
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
Join("JOIN organizations o ON o.id = users.org_id").
|
Join("JOIN organizations o ON o.id = users.org_id").
|
||||||
Where("users.id = ?", id)
|
Where("users.id = ?", id)
|
||||||
@ -343,7 +246,7 @@ func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
|||||||
Table("users").
|
Table("users").
|
||||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
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("g.name as role").
|
||||||
ColumnExpr("o.name as organization").
|
ColumnExpr("o.display_name as organization").
|
||||||
Join("JOIN groups g ON g.id = users.group_id").
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
Join("JOIN organizations o ON o.id = users.org_id").
|
Join("JOIN organizations o ON o.id = users.org_id").
|
||||||
Where("users.email = ?", email)
|
Where("users.email = ?", email)
|
||||||
@ -378,7 +281,7 @@ func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]t
|
|||||||
Table("users").
|
Table("users").
|
||||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
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("g.name as role").
|
||||||
ColumnExpr("o.name as organization").
|
ColumnExpr("o.display_name as organization").
|
||||||
Join("JOIN groups g ON g.id = users.group_id").
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
Join("JOIN organizations o ON o.id = users.org_id")
|
Join("JOIN organizations o ON o.id = users.org_id")
|
||||||
|
|
||||||
@ -402,7 +305,7 @@ func (mds *ModelDaoSqlite) GetUsersByOrg(ctx context.Context,
|
|||||||
Table("users").
|
Table("users").
|
||||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
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("g.name as role").
|
||||||
ColumnExpr("o.name as organization").
|
ColumnExpr("o.display_name as organization").
|
||||||
Join("JOIN groups g ON g.id = users.group_id").
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
Join("JOIN organizations o ON o.id = users.org_id").
|
Join("JOIN organizations o ON o.id = users.org_id").
|
||||||
Where("users.org_id = ?", orgId)
|
Where("users.org_id = ?", orgId)
|
||||||
@ -423,7 +326,7 @@ func (mds *ModelDaoSqlite) GetUsersByGroup(ctx context.Context,
|
|||||||
Table("users").
|
Table("users").
|
||||||
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
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("g.name as role").
|
||||||
ColumnExpr("o.name as organization").
|
ColumnExpr("o.display_name as organization").
|
||||||
Join("JOIN groups g ON g.id = users.group_id").
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
Join("JOIN organizations o ON o.id = users.org_id").
|
Join("JOIN organizations o ON o.id = users.org_id").
|
||||||
Where("users.group_id = ?", groupId)
|
Where("users.group_id = ?", groupId)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app"
|
"github.com/SigNoz/signoz/pkg/query-service/app"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/auth"
|
"github.com/SigNoz/signoz/pkg/query-service/auth"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||||
@ -313,7 +314,8 @@ func NewFilterSuggestionsTestBed(t *testing.T) *FilterSuggestionsTestBed {
|
|||||||
apiHandler.RegisterRoutes(router, am)
|
apiHandler.RegisterRoutes(router, am)
|
||||||
apiHandler.RegisterQueryRangeV3Routes(router, am)
|
apiHandler.RegisterQueryRangeV3Routes(router, am)
|
||||||
|
|
||||||
user, apiErr := createTestUser()
|
organizationModule := implorganization.NewModule(implorganization.NewStore(testDB))
|
||||||
|
user, apiErr := createTestUser(organizationModule)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
t.Fatalf("could not create a test user: %v", apiErr)
|
t.Fatalf("could not create a test user: %v", apiErr)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/agentConf"
|
"github.com/SigNoz/signoz/pkg/query-service/agentConf"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app"
|
"github.com/SigNoz/signoz/pkg/query-service/app"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
|
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
|
||||||
@ -479,7 +480,8 @@ func NewTestbedWithoutOpamp(t *testing.T, sqlStore sqlstore.SQLStore) *LogPipeli
|
|||||||
t.Fatalf("could not create a new ApiHandler: %v", err)
|
t.Fatalf("could not create a new ApiHandler: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
user, apiErr := createTestUser()
|
organizationModule := implorganization.NewModule(implorganization.NewStore(sqlStore))
|
||||||
|
user, apiErr := createTestUser(organizationModule)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
t.Fatalf("could not create a test user: %v", apiErr)
|
t.Fatalf("could not create a test user: %v", apiErr)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app"
|
"github.com/SigNoz/signoz/pkg/query-service/app"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
|
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/auth"
|
"github.com/SigNoz/signoz/pkg/query-service/auth"
|
||||||
@ -375,7 +377,8 @@ func NewCloudIntegrationsTestBed(t *testing.T, testDB sqlstore.SQLStore) *CloudI
|
|||||||
apiHandler.RegisterRoutes(router, am)
|
apiHandler.RegisterRoutes(router, am)
|
||||||
apiHandler.RegisterCloudIntegrationsRoutes(router, am)
|
apiHandler.RegisterCloudIntegrationsRoutes(router, am)
|
||||||
|
|
||||||
user, apiErr := createTestUser()
|
organizationModule := implorganization.NewModule(implorganization.NewStore(testDB))
|
||||||
|
user, apiErr := createTestUser(organizationModule)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
t.Fatalf("could not create a test user: %v", apiErr)
|
t.Fatalf("could not create a test user: %v", apiErr)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app"
|
"github.com/SigNoz/signoz/pkg/query-service/app"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
|
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
|
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
|
||||||
@ -583,7 +584,8 @@ func NewIntegrationsTestBed(t *testing.T, testDB sqlstore.SQLStore) *Integration
|
|||||||
apiHandler.RegisterRoutes(router, am)
|
apiHandler.RegisterRoutes(router, am)
|
||||||
apiHandler.RegisterIntegrationRoutes(router, am)
|
apiHandler.RegisterIntegrationRoutes(router, am)
|
||||||
|
|
||||||
user, apiErr := createTestUser()
|
organizationModule := implorganization.NewModule(implorganization.NewStore(testDB))
|
||||||
|
user, apiErr := createTestUser(organizationModule)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
t.Fatalf("could not create a test user: %v", apiErr)
|
t.Fatalf("could not create a test user: %v", apiErr)
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
|
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||||
"github.com/SigNoz/signoz/pkg/prometheus/prometheustest"
|
"github.com/SigNoz/signoz/pkg/prometheus/prometheustest"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app"
|
"github.com/SigNoz/signoz/pkg/query-service/app"
|
||||||
@ -148,19 +149,18 @@ func makeTestSignozLog(
|
|||||||
return testLog
|
return testLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestUser() (*types.User, *model.ApiError) {
|
func createTestUser(organizationModule organization.Module) (*types.User, *model.ApiError) {
|
||||||
// Create a test user for auth
|
// Create a test user for auth
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
org, apiErr := dao.DB().CreateOrg(ctx, &types.Organization{
|
organization := types.NewOrganization("test")
|
||||||
Name: "test",
|
err := organizationModule.Create(ctx, organization)
|
||||||
})
|
if err != nil {
|
||||||
if apiErr != nil {
|
return nil, model.InternalError(err)
|
||||||
return nil, apiErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group, apiErr := dao.DB().GetGroupByName(ctx, constants.AdminGroup)
|
group, apiErr := dao.DB().GetGroupByName(ctx, constants.AdminGroup)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
return nil, apiErr
|
return nil, model.InternalError(apiErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.InitAuthCache(ctx)
|
auth.InitAuthCache(ctx)
|
||||||
@ -174,7 +174,7 @@ func createTestUser() (*types.User, *model.ApiError) {
|
|||||||
Name: "test",
|
Name: "test",
|
||||||
Email: userId[:8] + "test@test.com",
|
Email: userId[:8] + "test@test.com",
|
||||||
Password: "test",
|
Password: "test",
|
||||||
OrgID: org.ID,
|
OrgID: organization.ID.StringValue(),
|
||||||
GroupID: group.ID,
|
GroupID: group.ID,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
@ -57,6 +57,7 @@ func NewTestSqliteDB(t *testing.T) (sqlStore sqlstore.SQLStore, testDBFilePath s
|
|||||||
sqlmigration.NewUpdatePatFactory(sqlStore),
|
sqlmigration.NewUpdatePatFactory(sqlStore),
|
||||||
sqlmigration.NewAddVirtualFieldsFactory(),
|
sqlmigration.NewAddVirtualFieldsFactory(),
|
||||||
sqlmigration.NewUpdateIntegrationsFactory(sqlStore),
|
sqlmigration.NewUpdateIntegrationsFactory(sqlStore),
|
||||||
|
sqlmigration.NewUpdateOrganizationsFactory(sqlStore),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -72,6 +72,7 @@ func NewSQLMigrationProviderFactories(sqlstore sqlstore.SQLStore) factory.NamedM
|
|||||||
sqlmigration.NewUpdateRulesFactory(sqlstore),
|
sqlmigration.NewUpdateRulesFactory(sqlstore),
|
||||||
sqlmigration.NewAddVirtualFieldsFactory(),
|
sqlmigration.NewAddVirtualFieldsFactory(),
|
||||||
sqlmigration.NewUpdateIntegrationsFactory(sqlstore),
|
sqlmigration.NewUpdateIntegrationsFactory(sqlstore),
|
||||||
|
sqlmigration.NewUpdateOrganizationsFactory(sqlstore),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
119
pkg/sqlmigration/028_update_organizations.go
Normal file
119
pkg/sqlmigration/028_update_organizations.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package sqlmigration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/migrate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type updateOrganizations struct {
|
||||||
|
store sqlstore.SQLStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateOrganizationsFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
|
||||||
|
return factory.NewProviderFactory(
|
||||||
|
factory.MustNewName("update_organizations"),
|
||||||
|
func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
|
||||||
|
return newUpdateOrganizations(ctx, ps, c, sqlstore)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUpdateOrganizations(_ context.Context, _ factory.ProviderSettings, _ Config, store sqlstore.SQLStore) (SQLMigration, error) {
|
||||||
|
return &updateOrganizations{store: store}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updateOrganizations) Register(migrations *migrate.Migrations) error {
|
||||||
|
if err := migrations.
|
||||||
|
Register(migration.Up, migration.Down); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updateOrganizations) Up(ctx context.Context, db *bun.DB) error {
|
||||||
|
tx, err := db.
|
||||||
|
BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
err = migration.
|
||||||
|
store.
|
||||||
|
Dialect().
|
||||||
|
DropColumn(ctx, tx, "organizations", "is_anonymous")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = migration.
|
||||||
|
store.
|
||||||
|
Dialect().
|
||||||
|
DropColumn(ctx, tx, "organizations", "has_opted_updates")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = migration.
|
||||||
|
store.
|
||||||
|
Dialect().
|
||||||
|
RenameColumn(ctx, tx, "organizations", "name", "display_name")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = migration.
|
||||||
|
store.
|
||||||
|
Dialect().
|
||||||
|
AddColumn(ctx, tx, "organizations", "name", "TEXT")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.
|
||||||
|
NewCreateIndex().
|
||||||
|
Unique().
|
||||||
|
IfNotExists().
|
||||||
|
Index("idx_unique_name").
|
||||||
|
Table("organizations").
|
||||||
|
Column("name").
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = migration.
|
||||||
|
store.
|
||||||
|
Dialect().
|
||||||
|
AddColumn(ctx, tx, "organizations", "alias", "TEXT")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tx.
|
||||||
|
NewCreateIndex().
|
||||||
|
Unique().
|
||||||
|
IfNotExists().
|
||||||
|
Index("idx_unique_alias").
|
||||||
|
Table("organizations").
|
||||||
|
Column("alias").
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updateOrganizations) Down(context.Context, *bun.DB) error {
|
||||||
|
return nil
|
||||||
|
}
|
@ -74,6 +74,14 @@ func (dialect *dialect) MigrateIntToTimestamp(ctx context.Context, bun bun.IDB,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dialect *dialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
func (dialect *dialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
columnExists, err := dialect.ColumnExists(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !columnExists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -141,6 +149,26 @@ func (dialect *dialect) ColumnExists(ctx context.Context, bun bun.IDB, table str
|
|||||||
return count > 0, nil
|
return count > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dialect *dialect) AddColumn(ctx context.Context, bun bun.IDB, table string, column string, columnExpr string) error {
|
||||||
|
exists, err := dialect.ColumnExists(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
_, err = bun.
|
||||||
|
NewAddColumn().
|
||||||
|
Table(table).
|
||||||
|
ColumnExpr(column + " " + columnExpr).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table string, oldColumnName string, newColumnName string) (bool, error) {
|
func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table string, oldColumnName string, newColumnName string) (bool, error) {
|
||||||
oldColumnExists, err := dialect.ColumnExists(ctx, bun, table, oldColumnName)
|
oldColumnExists, err := dialect.ColumnExists(ctx, bun, table, oldColumnName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -152,10 +180,14 @@ func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table str
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !oldColumnExists && newColumnExists {
|
if newColumnExists {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !oldColumnExists {
|
||||||
|
return false, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "old column: %s doesn't exist", oldColumnName)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = bun.
|
_, err = bun.
|
||||||
ExecContext(ctx, "ALTER TABLE "+table+" RENAME COLUMN "+oldColumnName+" TO "+newColumnName)
|
ExecContext(ctx, "ALTER TABLE "+table+" RENAME COLUMN "+oldColumnName+" TO "+newColumnName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -164,6 +196,26 @@ func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table str
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dialect *dialect) DropColumn(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
exists, err := dialect.ColumnExists(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
_, err = bun.
|
||||||
|
NewDropColumn().
|
||||||
|
Table(table).
|
||||||
|
Column(column).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dialect *dialect) TableExists(ctx context.Context, bun bun.IDB, table interface{}) (bool, error) {
|
func (dialect *dialect) TableExists(ctx context.Context, bun bun.IDB, table interface{}) (bool, error) {
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
|
@ -42,7 +42,9 @@ type SQLDialect interface {
|
|||||||
AddNotNullDefaultToColumn(context.Context, bun.IDB, string, string, string, string) error
|
AddNotNullDefaultToColumn(context.Context, bun.IDB, string, string, string, string) error
|
||||||
GetColumnType(context.Context, bun.IDB, string, string) (string, error)
|
GetColumnType(context.Context, bun.IDB, string, string) (string, error)
|
||||||
ColumnExists(context.Context, bun.IDB, string, string) (bool, error)
|
ColumnExists(context.Context, bun.IDB, string, string) (bool, error)
|
||||||
|
AddColumn(context.Context, bun.IDB, string, string, string) error
|
||||||
RenameColumn(context.Context, bun.IDB, string, string, string) (bool, error)
|
RenameColumn(context.Context, bun.IDB, string, string, string) (bool, error)
|
||||||
|
DropColumn(context.Context, bun.IDB, string, string) error
|
||||||
RenameTableAndModifyModel(context.Context, bun.IDB, interface{}, interface{}, []string, func(context.Context) error) error
|
RenameTableAndModifyModel(context.Context, bun.IDB, interface{}, interface{}, []string, func(context.Context) error) error
|
||||||
UpdatePrimaryKey(context.Context, bun.IDB, interface{}, interface{}, string, func(context.Context) error) error
|
UpdatePrimaryKey(context.Context, bun.IDB, interface{}, interface{}, string, func(context.Context) error) error
|
||||||
AddPrimaryKey(context.Context, bun.IDB, interface{}, interface{}, string, func(context.Context) error) error
|
AddPrimaryKey(context.Context, bun.IDB, interface{}, interface{}, string, func(context.Context) error) error
|
||||||
|
@ -25,10 +25,18 @@ func (dialect *dialect) ColumnExists(ctx context.Context, bun bun.IDB, table str
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dialect *dialect) AddColumn(ctx context.Context, bun bun.IDB, table string, column string, columnExpr string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table string, oldColumnName string, newColumnName string) (bool, error) {
|
func (dialect *dialect) RenameColumn(ctx context.Context, bun bun.IDB, table string, oldColumnName string, newColumnName string) (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dialect *dialect) DropColumn(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dialect *dialect) RenameTableAndModifyModel(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, references []string, cb func(context.Context) error) error {
|
func (dialect *dialect) RenameTableAndModifyModel(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, references []string, cb func(context.Context) error) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,34 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: check constraints are not working
|
|
||||||
type Organization struct {
|
type Organization struct {
|
||||||
bun.BaseModel `bun:"table:organizations"`
|
bun.BaseModel `bun:"table:organizations"`
|
||||||
TimeAuditable
|
TimeAuditable
|
||||||
ID string `bun:"id,pk,type:text" json:"id"`
|
Identifiable
|
||||||
Name string `bun:"name,type:text,notnull" json:"name"`
|
Name string `bun:"name,type:text,nullzero" json:"name"`
|
||||||
IsAnonymous bool `bun:"is_anonymous,notnull,default:0,CHECK(is_anonymous IN (0,1))" json:"isAnonymous"`
|
Alias string `bun:"alias,type:text,nullzero" json:"alias"`
|
||||||
HasOptedUpdates bool `bun:"has_opted_updates,notnull,default:1,CHECK(has_opted_updates IN (0,1))" json:"hasOptedUpdates"`
|
DisplayName string `bun:"display_name,type:text,notnull" json:"displayName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrganization(displayName string) *Organization {
|
||||||
|
return &Organization{
|
||||||
|
Identifiable: Identifiable{
|
||||||
|
ID: valuer.GenerateUUID(),
|
||||||
|
},
|
||||||
|
TimeAuditable: TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
// Name: "default/main", TODO: take the call and uncomment this later
|
||||||
|
DisplayName: displayName,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApdexSettings struct {
|
type ApdexSettings struct {
|
||||||
@ -22,3 +39,11 @@ type ApdexSettings struct {
|
|||||||
Threshold float64 `bun:"threshold,type:float,notnull" json:"threshold"`
|
Threshold float64 `bun:"threshold,type:float,notnull" json:"threshold"`
|
||||||
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull" json:"excludeStatusCodes"`
|
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull" json:"excludeStatusCodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrganizationStore interface {
|
||||||
|
Create(context.Context, *Organization) error
|
||||||
|
Get(context.Context, valuer.UUID) (*Organization, error)
|
||||||
|
GetAll(context.Context) ([]*Organization, error)
|
||||||
|
Update(context.Context, *Organization) error
|
||||||
|
Delete(context.Context, valuer.UUID) error
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user