diff --git a/id/client.go b/id/client.go new file mode 100644 index 0000000000000000000000000000000000000000..56d0e2b283f82ea65c6e1cd892f06378b0346bb8 --- /dev/null +++ b/id/client.go @@ -0,0 +1,77 @@ +package id + +import ( + jsoniter "github.com/json-iterator/go" +) + +const ( + Client = "client" + ClientsPrefix = "clients" +) + +type ClientID struct { + SpaceID + ClientID string `json:"client_id"` +} + +func (t *ClientID) Type() string { return Client } + +func (t *ClientID) String() string { + return Join(t.SpaceID.String(), ClientsPrefix, t.ClientID) + +} + +func (t *ClientID) ToMap() map[string]any { + m := t.SpaceID.ToMap() + m["client_id"] = t.ClientID + return m +} + +func (t *ClientID) FromMap(m map[string]any) error { + if err := t.SpaceID.FromMap(m); err != nil { + return err + } + t.ClientID = m["client_id"].(string) + return nil +} + +func (t *ClientID) Validate() error { + if t.ClientID == "" { + return ErrInvalidID + } + + return t.SpaceID.Validate() +} + +func parseClientID(parts []string) (*ClientID, error) { + if len(parts) != 4 || parts[2] != ClientsPrefix { + return nil, ErrInvalidID + } + + spaceID, err := parseSpaceID(parts[:2]) + if err != nil { + return nil, err + } + + var id ClientID + id.SpaceID = *spaceID + id.ClientID = parts[3] + return &id, nil +} +func (t *ClientID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.SpaceID = knownID.(*ClientID).SpaceID + t.ClientID = knownID.(*ClientID).ClientID + return nil +} + +func (t *ClientID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/collection.go b/id/collection.go index 50bf64ee6b7c02cd5629120ccf288155d85877a8..029735ba377fa3996f0ba072be793992f6a803d8 100644 --- a/id/collection.go +++ b/id/collection.go @@ -1,5 +1,9 @@ package id +import ( + jsoniter "github.com/json-iterator/go" +) + const ( Collection = "collection" CollectionsPrefix = "cols" @@ -18,7 +22,7 @@ func (t *CollectionID) String() string { func (t *CollectionID) ToMap() map[string]any { m := t.EnvironmentID.ToMap() - m["col_id"] = t.EnvironmentID + m["col_id"] = t.CollectionID return m } @@ -39,7 +43,7 @@ func (t *CollectionID) Validate() error { } func parseCollectionID(parts []string) (*CollectionID, error) { - if len(parts) != 6 || parts[0] != EnvironmentsPrefix { + if len(parts) != 6 || parts[4] != CollectionsPrefix { return nil, ErrInvalidID } @@ -53,3 +57,21 @@ func parseCollectionID(parts []string) (*CollectionID, error) { id.EnvironmentID = *envID return &id, nil } + +func (t *CollectionID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.EnvironmentID = knownID.(*CollectionID).EnvironmentID + t.CollectionID = knownID.(*CollectionID).CollectionID + return nil +} + +func (t *CollectionID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/environment.go b/id/environment.go index 4958a33f73b4e2c8d8edbb76bda7b1d7705cfbd2..b8b08e83012208e217f9b33d54f200fe2d3d6ff1 100644 --- a/id/environment.go +++ b/id/environment.go @@ -1,5 +1,9 @@ package id +import ( + jsoniter "github.com/json-iterator/go" +) + const ( Environment = "environment" EnvironmentsPrefix = "envs" @@ -13,9 +17,7 @@ type EnvironmentID struct { func (t *EnvironmentID) Type() string { return Environment } func (t *EnvironmentID) String() string { - return Join(t.SpaceID.String(), - EnvironmentsPrefix, t.EnvironmentID, - ) + return Join(t.SpaceID.String(), EnvironmentsPrefix, t.EnvironmentID) } @@ -56,3 +58,21 @@ func parseEnvironmentID(parts []string) (*EnvironmentID, error) { id.SpaceID = *spaceID return &id, nil } + +func (t *EnvironmentID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.SpaceID = knownID.(*EnvironmentID).SpaceID + t.EnvironmentID = knownID.(*EnvironmentID).EnvironmentID + return nil +} + +func (t *EnvironmentID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/field.go b/id/field.go new file mode 100644 index 0000000000000000000000000000000000000000..d1539a2c8720809b9396c1dd5fdb2087f5a0cfab --- /dev/null +++ b/id/field.go @@ -0,0 +1,78 @@ +package id + +import ( + jsoniter "github.com/json-iterator/go" +) + +const ( + Field = "field" + FieldsPrefix = "fields" +) + +type FieldID struct { + ItemID + FieldName string `json:"field_name"` +} + +func (t *FieldID) Type() string { return Field } + +func (t *FieldID) String() string { + return Join(t.ItemID.String(), FieldsPrefix, t.FieldName) + +} + +func (t *FieldID) ToMap() map[string]any { + m := t.ItemID.ToMap() + m["field_name"] = t.FieldName + return m +} + +func (t *FieldID) FromMap(m map[string]any) error { + if err := t.ItemID.FromMap(m); err != nil { + return err + } + t.FieldName = m["field_name"].(string) + return nil +} + +func (t *FieldID) Validate() error { + if t.FieldName == "" { + return ErrInvalidID + } + + return t.ItemID.Validate() +} + +func parseFieldID(parts []string) (*FieldID, error) { + if len(parts) != 10 || parts[8] != FieldsPrefix { + return nil, ErrInvalidID + } + + itemID, err := parseItemID(parts[:8]) + if err != nil { + return nil, err + } + + var id FieldID + id.ItemID = *itemID + id.FieldName = parts[9] + return &id, nil +} + +func (t *FieldID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.ItemID = knownID.(*FieldID).ItemID + t.FieldName = knownID.(*FieldID).FieldName + return nil +} + +func (t *FieldID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/id.go b/id/id.go index a4ff8a5e16f7ef9959560b51b86f0d8f1a0e9137..88cad22a4e5a0a4b8363f07d08d5aa311b0d8173 100644 --- a/id/id.go +++ b/id/id.go @@ -1,13 +1,16 @@ package id import ( - "errors" "strings" + + "git.perx.ru/perxis/perxis-go/pkg/errors" ) const Separator = '/' -var ErrInvalidID = errors.New("invalid id") +var ( + ErrInvalidID = errors.New("invalid id") +) type ID interface { String() string @@ -20,9 +23,19 @@ type ID interface { func Parse(s string) (ID, error) { parts := Split(s) - // TODO: Парсим строку, от коротких к длинным + if id, _ := parseServiceID(parts); id != nil { + return id, nil + } + + if id, _ := parseUserID(parts); id != nil { + return id, nil + } + + if id, _ := parseOrganizationID(parts); id != nil { + return id, nil + } - if id, err := parseSpaceID(parts); err != nil { + if id, _ := parseSpaceID(parts); id != nil { return id, nil } @@ -30,10 +43,34 @@ func Parse(s string) (ID, error) { return id, nil } + if id, _ := parseClientID(parts); id != nil { + return id, nil + } + + if id, _ := parseRoleID(parts); id != nil { + return id, nil + } + if id, _ := parseCollectionID(parts); id != nil { return id, nil } + if id, _ := parseSchemaID(parts); id != nil { + return id, nil + } + + if id, _ := parseItemID(parts); id != nil { + return id, nil + } + + if id, _ := parseRevisionID(parts); id != nil { + return id, nil + } + + if id, _ := parseFieldID(parts); id != nil { + return id, nil + } + return nil, ErrInvalidID } diff --git a/id/id_test.go b/id/id_test.go new file mode 100644 index 0000000000000000000000000000000000000000..181c7459b7b4580c4045ffed112e62d30a86496f --- /dev/null +++ b/id/id_test.go @@ -0,0 +1,481 @@ +package id + +import ( + "encoding/json" + "testing" + + jsoniter "github.com/json-iterator/go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_ParseID(t *testing.T) { + tests := []struct { + name string + id string + result ID + }{ + { + name: Service, + id: "/services/<service_id>", + result: &ServiceID{ServiceID: "<service_id>"}, + }, + { + name: User, + id: "/users/<user_id>", + result: &UserID{UserID: "<user_id>"}, + }, + { + name: Organization, + id: "/orgs/<org_id>", + result: &OrganizationID{OrganizationID: "<org_id>"}, + }, + { + name: Space, + id: "/spaces/<space_id>", + result: &SpaceID{SpaceID: "<space_id>"}, + }, + { + name: Client, + id: "/spaces/<space_id>/clients/<client_id>", + result: &ClientID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + ClientID: "<client_id>", + }, + }, + { + name: Role, + id: "/spaces/<space_id>/roles/<role_id>", + result: &RoleID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + RoleID: "<role_id>", + }, + }, + { + name: Environment, + id: "/spaces/<space_id>/envs/<env_id>", + result: &EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + }, + { + name: Collection, + id: "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>", + result: &CollectionID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + CollectionID: "<collection_id>", + }, + }, + { + name: Schema, + id: "/spaces/<space_id>/envs/<env_id>/schema/<collection_id>", + result: &SchemaID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + SchemaID: "<collection_id>", + }, + }, + { + name: Item, + id: "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>", + result: &ItemID{ + CollectionID: CollectionID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + CollectionID: "<collection_id>", + }, + ItemID: "<item_id>", + }, + }, + { + name: Revision, + id: "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/revs/<rev_id>", + result: &RevisionID{ + ItemID: ItemID{ + CollectionID: CollectionID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + CollectionID: "<collection_id>", + }, + ItemID: "<item_id>", + }, + RevisionID: "<rev_id>", + }, + }, + { + name: Field, + id: "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/fields/<field_name>", + result: &FieldID{ + ItemID: ItemID{ + CollectionID: CollectionID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + CollectionID: "<collection_id>", + }, + ItemID: "<item_id>", + }, + FieldName: "<field_name>", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + id, _ := Parse(tt.id) + require.Equal(t, tt.result, id) + require.Equal(t, tt.id, id.String(), "проверяем корректность работы метода String, полученное ID должно совпадать с исходным") + }) + } +} + +func Test_Map(t *testing.T) { + tests := []struct { + name string + id ID + from map[string]any + to ID + }{ + { + name: Service, + id: &ServiceID{}, + from: map[string]any{"service_id": "<service_id>"}, + to: &ServiceID{ServiceID: "<service_id>"}, + }, + { + name: User, + id: &UserID{}, + from: map[string]any{"user_id": "<user_id>"}, + to: &UserID{UserID: "<user_id>"}, + }, + { + name: Organization, + id: &OrganizationID{}, + from: map[string]any{"organization_id": "<org_id>"}, + to: &OrganizationID{OrganizationID: "<org_id>"}, + }, + { + name: Space, + id: &SpaceID{}, + from: map[string]any{"space_id": "<space_id>"}, + to: &SpaceID{SpaceID: "<space_id>"}, + }, + { + name: Client, + id: &ClientID{}, + from: map[string]any{"space_id": "<space_id>", "client_id": "<client_id>"}, + to: &ClientID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + ClientID: "<client_id>", + }, + }, + { + name: Role, + id: &RoleID{}, + from: map[string]any{"space_id": "<space_id>", "role_id": "<role_id>"}, + to: &RoleID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + RoleID: "<role_id>", + }, + }, + { + name: Environment, + id: &EnvironmentID{}, + from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>"}, + to: &EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + }, + { + name: Collection, + id: &CollectionID{}, + from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "col_id": "<collection_id>"}, + to: &CollectionID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + CollectionID: "<collection_id>", + }, + }, + { + name: Schema, + id: &SchemaID{}, + from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "schema_id": "<collection_id>"}, + to: &SchemaID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + SchemaID: "<collection_id>", + }, + }, + { + name: Item, + id: &ItemID{}, + from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "col_id": "<collection_id>", "item_id": "<item_id>"}, + to: &ItemID{ + CollectionID: CollectionID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + CollectionID: "<collection_id>", + }, + ItemID: "<item_id>", + }, + }, + { + name: Revision, + id: &RevisionID{}, + from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "col_id": "<collection_id>", "item_id": "<item_id>", "rev_id": "<rev_id>"}, + to: &RevisionID{ + ItemID: ItemID{ + CollectionID: CollectionID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + CollectionID: "<collection_id>", + }, + ItemID: "<item_id>", + }, + RevisionID: "<rev_id>", + }, + }, + { + name: Field, + id: &FieldID{}, + from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "col_id": "<collection_id>", "item_id": "<item_id>", "field_name": "<field_name>"}, + to: &FieldID{ + ItemID: ItemID{ + CollectionID: CollectionID{ + EnvironmentID: EnvironmentID{ + SpaceID: SpaceID{SpaceID: "<space_id>"}, + EnvironmentID: "<env_id>", + }, + CollectionID: "<collection_id>", + }, + ItemID: "<item_id>", + }, + FieldName: "<field_name>", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := tt.id + require.NoError(t, v.FromMap(tt.from)) + require.Equal(t, tt.to, v) + require.Equal(t, v.ToMap(), tt.from) + }) + } +} + +func Test_MarshalJSON(t *testing.T) { + tests := []struct { + name string + json string + id json.Marshaler + }{ + { + Organization, + "/orgs/<org_id>", + &OrganizationID{OrganizationID: "<org_id>"}, + }, + { + Service, + "/services/<service_id>", + &ServiceID{ServiceID: "<service_id>"}, + }, + { + Space, + "/spaces/<space_id>", + &SpaceID{SpaceID: "<space_id>"}, + }, + { + Environment, + "/spaces/<space_id>/envs/<env_id>", + &EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, + }, + { + Client, + "/spaces/<space_id>/clients/<client_id>", + &ClientID{SpaceID: SpaceID{SpaceID: "<space_id>"}, ClientID: "<client_id>"}, + }, + { + Role, + "/spaces/<space_id>/roles/<role_id>", + &RoleID{SpaceID: SpaceID{SpaceID: "<space_id>"}, RoleID: "<role_id>"}, + }, + { + Collection, + "/spaces/<space_id>/envs/<env_id>/cols/<coll_id>", + &CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, + }, + { + Schema, + "/spaces/<space_id>/envs/<env_id>/schema/<coll_id>", + &SchemaID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, SchemaID: "<coll_id>"}, + }, + { + Item, + "/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>", + &ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, + }, + { + Revision, + "/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>/revs/<rev_id>", + &RevisionID{ItemID: ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, RevisionID: "<rev_id>"}, + }, + { + Field, + "/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>/fields/<field_name>", + &FieldID{ItemID: ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, FieldName: "<field_name>"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b, err := jsoniter.Marshal(tt.id) + require.NoError(t, err) + assert.Equal(t, tt.json, string(b)) + }) + } +} + +func Test_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + json string + id json.Unmarshaler + }{ + { + Organization, + "\"/orgs/<org_id>\"", + &OrganizationID{OrganizationID: "<org_id>"}, + }, + { + Service, + "\"/services/<service_id>\"", + &ServiceID{ServiceID: "<service_id>"}, + }, + { + Space, + "\"/spaces/<space_id>\"", + &SpaceID{SpaceID: "<space_id>"}, + }, + { + Environment, + "\"/spaces/<space_id>/envs/<env_id>\"", + &EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, + }, + { + Client, + "\"/spaces/<space_id>/clients/<client_id>\"", + &ClientID{SpaceID: SpaceID{SpaceID: "<space_id>"}, ClientID: "<client_id>"}, + }, + { + Role, + "\"/spaces/<space_id>/roles/<role_id>\"", + &RoleID{SpaceID: SpaceID{SpaceID: "<space_id>"}, RoleID: "<role_id>"}, + }, + { + Collection, + "\"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>\"", + &CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, + }, + { + Schema, + "\"/spaces/<space_id>/envs/<env_id>/schema/<coll_id>\"", + &SchemaID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, SchemaID: "<coll_id>"}, + }, + { + Item, + "\"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>\"", + &ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, + }, + { + Revision, + "\"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>/revs/<rev_id>\"", + &RevisionID{ItemID: ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, RevisionID: "<rev_id>"}, + }, + { + Field, + "\"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>/fields/<field_name>\"", + &FieldID{ItemID: ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, FieldName: "<field_name>"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch tt.name { + case Organization: + v := new(OrganizationID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Service: + v := new(ServiceID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Space: + v := new(SpaceID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Environment: + v := new(EnvironmentID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Client: + v := new(ClientID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Role: + v := new(RoleID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Collection: + v := new(CollectionID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Schema: + v := new(SchemaID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Item: + v := new(ItemID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Revision: + v := new(RevisionID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + case Field: + v := new(FieldID) + err := v.UnmarshalJSON([]byte(tt.json)) + require.NoError(t, err) + assert.Equal(t, tt.id, v) + } + }) + } +} diff --git a/id/item.go b/id/item.go new file mode 100644 index 0000000000000000000000000000000000000000..224c3e19e360cceb7f89f56f9a552f47be6dc308 --- /dev/null +++ b/id/item.go @@ -0,0 +1,77 @@ +package id + +import ( + jsoniter "github.com/json-iterator/go" +) + +const ( + Item = "item" + ItemsPrefix = "items" +) + +type ItemID struct { + CollectionID + ItemID string `json:"item_id"` +} + +func (t *ItemID) Type() string { return Item } + +func (t *ItemID) String() string { + return Join(t.CollectionID.String(), ItemsPrefix, t.ItemID) + +} + +func (t *ItemID) ToMap() map[string]any { + m := t.CollectionID.ToMap() + m["item_id"] = t.ItemID + return m +} + +func (t *ItemID) FromMap(m map[string]any) error { + if err := t.CollectionID.FromMap(m); err != nil { + return err + } + t.ItemID = m["item_id"].(string) + return nil +} + +func (t *ItemID) Validate() error { + if t.ItemID == "" { + return ErrInvalidID + } + + return t.CollectionID.Validate() +} + +func parseItemID(parts []string) (*ItemID, error) { + if len(parts) != 8 || parts[6] != ItemsPrefix { + return nil, ErrInvalidID + } + + collID, err := parseCollectionID(parts[:6]) + if err != nil { + return nil, err + } + + var id ItemID + id.CollectionID = *collID + id.ItemID = parts[7] + return &id, nil +} +func (t *ItemID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.CollectionID = knownID.(*ItemID).CollectionID + t.ItemID = knownID.(*ItemID).ItemID + return nil +} + +func (t *ItemID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/json.go b/id/json.go index 04fa39faf5f812a26aa1f9cc2bb1e3cf98f59f1e..b64bd2a1d7888d90c05072d3c86f7810f5de1318 100644 --- a/id/json.go +++ b/id/json.go @@ -1,3 +1,6 @@ package id // TODO: Сохранение/чтение в JSON (в строку) +var knownImplementations = []ID{ + &SpaceID{}, +} diff --git a/id/organization.go b/id/organization.go new file mode 100644 index 0000000000000000000000000000000000000000..2c88414031f9a0ca9d5d3a5a87e6cf20eabf4b29 --- /dev/null +++ b/id/organization.go @@ -0,0 +1,64 @@ +package id + +import ( + jsoniter "github.com/json-iterator/go" +) + +const ( + Organization = "organization" + OrganizationsPrefix = "orgs" +) + +type OrganizationID struct { + OrganizationID string `json:"organization_id"` +} + +func (t *OrganizationID) Type() string { return Organization } + +func (t *OrganizationID) String() string { + return Join(OrganizationsPrefix, t.OrganizationID) +} + +func (t *OrganizationID) ToMap() map[string]any { + return map[string]any{ + "organization_id": t.OrganizationID, + } +} + +func (t *OrganizationID) FromMap(m map[string]any) error { + t.OrganizationID = m["organization_id"].(string) + return nil +} + +func (t *OrganizationID) Validate() error { + if t.OrganizationID == "" { + return ErrInvalidID + } + return nil +} + +func parseOrganizationID(parts []string) (*OrganizationID, error) { + var id OrganizationID + if len(parts) != 2 || parts[0] != OrganizationsPrefix { + return nil, ErrInvalidID + } + + id.OrganizationID = parts[1] + return &id, nil +} +func (t *OrganizationID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.OrganizationID = knownID.(*OrganizationID).OrganizationID + return nil +} + +func (t *OrganizationID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/revision.go b/id/revision.go new file mode 100644 index 0000000000000000000000000000000000000000..a5a1caf1b0376da20400d24b33b4ef5346db5386 --- /dev/null +++ b/id/revision.go @@ -0,0 +1,78 @@ +package id + +import ( + jsoniter "github.com/json-iterator/go" +) + +const ( + Revision = "revision" + RevisionsPrefix = "revs" +) + +type RevisionID struct { + ItemID + RevisionID string `json:"rev_id"` +} + +func (t *RevisionID) Type() string { return Revision } + +func (t *RevisionID) String() string { + return Join(t.ItemID.String(), RevisionsPrefix, t.RevisionID) + +} + +func (t *RevisionID) ToMap() map[string]any { + m := t.ItemID.ToMap() + m["rev_id"] = t.RevisionID + return m +} + +func (t *RevisionID) FromMap(m map[string]any) error { + if err := t.ItemID.FromMap(m); err != nil { + return err + } + t.RevisionID = m["rev_id"].(string) + return nil +} + +func (t *RevisionID) Validate() error { + if t.RevisionID == "" { + return ErrInvalidID + } + + return t.ItemID.Validate() +} + +func parseRevisionID(parts []string) (*RevisionID, error) { + if len(parts) != 10 || parts[8] != RevisionsPrefix { + return nil, ErrInvalidID + } + + itemID, err := parseItemID(parts[:8]) + if err != nil { + return nil, err + } + + var id RevisionID + id.ItemID = *itemID + id.RevisionID = parts[9] + return &id, nil +} + +func (t *RevisionID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.ItemID = knownID.(*RevisionID).ItemID + t.RevisionID = knownID.(*RevisionID).RevisionID + return nil +} + +func (t *RevisionID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/role.go b/id/role.go new file mode 100644 index 0000000000000000000000000000000000000000..273f5e1e071943887ea8702fd266620aba86eb05 --- /dev/null +++ b/id/role.go @@ -0,0 +1,78 @@ +package id + +import ( + jsoniter "github.com/json-iterator/go" +) + +const ( + Role = "role" + RolesPrefix = "roles" +) + +type RoleID struct { + SpaceID + RoleID string `json:"role_id"` +} + +func (t *RoleID) Type() string { return Client } + +func (t *RoleID) String() string { + return Join(t.SpaceID.String(), RolesPrefix, t.RoleID) + +} + +func (t *RoleID) ToMap() map[string]any { + m := t.SpaceID.ToMap() + m["role_id"] = t.RoleID + return m +} + +func (t *RoleID) FromMap(m map[string]any) error { + if err := t.SpaceID.FromMap(m); err != nil { + return err + } + t.RoleID = m["role_id"].(string) + return nil +} + +func (t *RoleID) Validate() error { + if t.RoleID == "" { + return ErrInvalidID + } + + return t.SpaceID.Validate() +} + +func parseRoleID(parts []string) (*RoleID, error) { + if len(parts) != 4 || parts[2] != RolesPrefix { + return nil, ErrInvalidID + } + + spaceID, err := parseSpaceID(parts[:2]) + if err != nil { + return nil, err + } + + var id RoleID + id.SpaceID = *spaceID + id.RoleID = parts[3] + return &id, nil +} + +func (t *RoleID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.SpaceID = knownID.(*RoleID).SpaceID + t.RoleID = knownID.(*RoleID).RoleID + return nil +} + +func (t *RoleID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/schema.go b/id/schema.go new file mode 100644 index 0000000000000000000000000000000000000000..43c97a4f0b8bd859937585d2771f7e4301d69a81 --- /dev/null +++ b/id/schema.go @@ -0,0 +1,78 @@ +package id + +import ( + jsoniter "github.com/json-iterator/go" +) + +const ( + Schema = "schema" + SchemaPrefix = "schema" +) + +type SchemaID struct { + SpaceID + EnvironmentID + SchemaID string `json:"schema_id"` +} + +func (t *SchemaID) Type() string { return Schema } + +func (t *SchemaID) String() string { + return Join(t.EnvironmentID.String(), SchemaPrefix, t.SchemaID) +} + +func (t *SchemaID) ToMap() map[string]any { + m := t.EnvironmentID.ToMap() + m["schema_id"] = t.SchemaID + return m +} + +func (t *SchemaID) FromMap(m map[string]any) error { + if err := t.EnvironmentID.FromMap(m); err != nil { + return err + } + t.SchemaID = m["schema_id"].(string) + return nil +} + +func (t *SchemaID) Validate() error { + if t.SchemaID == "" { + return ErrInvalidID + } + + return t.EnvironmentID.Validate() +} + +func parseSchemaID(parts []string) (*SchemaID, error) { + if len(parts) != 6 || parts[4] != SchemaPrefix { + return nil, ErrInvalidID + } + + envID, err := parseEnvironmentID(parts[:4]) + if err != nil { + return nil, err + } + + var id SchemaID + id.EnvironmentID = *envID + id.SchemaID = parts[5] + return &id, nil +} + +func (t *SchemaID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.EnvironmentID = knownID.(*SchemaID).EnvironmentID + t.SchemaID = knownID.(*SchemaID).SchemaID + return nil +} + +func (t *SchemaID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/service.go b/id/service.go new file mode 100644 index 0000000000000000000000000000000000000000..9a55357bea06564f89d0958a2e8be9e2dcb247bf --- /dev/null +++ b/id/service.go @@ -0,0 +1,64 @@ +package id + +import ( + jsoniter "github.com/json-iterator/go" +) + +const ( + Service = "service" + ServicesPrefix = "services" +) + +type ServiceID struct { + ServiceID string `json:"service_id"` +} + +func (t *ServiceID) Type() string { return Service } + +func (t *ServiceID) String() string { + return Join(ServicesPrefix, t.ServiceID) +} + +func (t *ServiceID) ToMap() map[string]any { + return map[string]any{ + "service_id": t.ServiceID, + } +} + +func (t *ServiceID) FromMap(m map[string]any) error { + t.ServiceID = m["service_id"].(string) + return nil +} + +func (t *ServiceID) Validate() error { + if t.ServiceID == "" { + return ErrInvalidID + } + return nil +} + +func parseServiceID(parts []string) (*ServiceID, error) { + var id ServiceID + if len(parts) != 2 || parts[0] != ServicesPrefix { + return nil, ErrInvalidID + } + + id.ServiceID = parts[1] + return &id, nil +} +func (t *ServiceID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.ServiceID = knownID.(*ServiceID).ServiceID + return nil +} + +func (t *ServiceID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/space.go b/id/space.go index 3d9d6c35cba03a45d1cee00d692865e0fc8a5037..401fd58d9254e78b4691590206ba4c656ff92096 100644 --- a/id/space.go +++ b/id/space.go @@ -1,5 +1,9 @@ package id +import ( + jsoniter "github.com/json-iterator/go" +) + const ( Space = "space" SpacesPrefix = "spaces" @@ -42,3 +46,20 @@ func parseSpaceID(parts []string) (*SpaceID, error) { id.SpaceID = parts[1] return &id, nil } + +func (t *SpaceID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.SpaceID = knownID.(*SpaceID).SpaceID + return nil +} + +func (t *SpaceID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/id/user.go b/id/user.go new file mode 100644 index 0000000000000000000000000000000000000000..6ea9ea529c0d4606a5bfee14262c0405d5d5d812 --- /dev/null +++ b/id/user.go @@ -0,0 +1,65 @@ +package id + +import ( + jsoniter "github.com/json-iterator/go" +) + +const ( + User = "user" + UsersPrefix = "users" +) + +type UserID struct { + UserID string `json:"user_id"` +} + +func (t *UserID) Type() string { return User } + +func (t *UserID) String() string { + return Join(UsersPrefix, t.UserID) +} + +func (t *UserID) ToMap() map[string]any { + return map[string]any{ + "user_id": t.UserID, + } +} + +func (t *UserID) FromMap(m map[string]any) error { + t.UserID = m["user_id"].(string) + return nil +} + +func (t *UserID) Validate() error { + if t.UserID == "" { + return ErrInvalidID + } + return nil +} + +func parseUserID(parts []string) (*UserID, error) { + var id UserID + if len(parts) != 2 || parts[0] != UsersPrefix { + return nil, ErrInvalidID + } + + id.UserID = parts[1] + return &id, nil +} + +func (t *UserID) UnmarshalJSON(b []byte) error { + var data string + if err := jsoniter.Unmarshal(b, &data); err != nil { + return err + } + knownID, err := Parse(data) + if err != nil { + return err + } + t.UserID = knownID.(*UserID).UserID + return nil +} + +func (t *UserID) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} diff --git a/perxis-proto b/perxis-proto index ecd756865a06583ce52bff6265c0fd5e159d93b9..63410745d6008eaa9d9b00626b5f5b6891ac9189 160000 --- a/perxis-proto +++ b/perxis-proto @@ -1 +1 @@ -Subproject commit ecd756865a06583ce52bff6265c0fd5e159d93b9 +Subproject commit 63410745d6008eaa9d9b00626b5f5b6891ac9189