Skip to content

Commit 3a3bdb9

Browse files
authored
Refactoring Client/Server Operation model (#201)
* Refactoring Client/Server Operation model * Operation Model Refactor
1 parent 6146a42 commit 3a3bdb9

17 files changed

+240
-185
lines changed

backend/OTClient/operationModel.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// integer operation
2+
type integerOperation = {
3+
newValue: number
4+
}
5+
6+
type booleanOperation = {
7+
newValue: boolean
8+
}
9+
10+
// string based operations
11+
type stringOperation = {
12+
operationType: "insert" | "delete";
13+
rangeStart: number, rangeEnd: number;
14+
15+
newValue?: string;
16+
}
17+
18+
// array based operations
19+
type arrayOperation = {
20+
index: number
21+
22+
payload: {
23+
$type: string
24+
value: object
25+
}
26+
}
27+
28+
// object based operations
29+
type objectOperation = {
30+
$type: string,
31+
value: object
32+
}
33+
34+
// the generic operation type
35+
type operation = {
36+
$type: "integerOperation" | "booleanOperation" |
37+
"arrayOperation" | "objectOperation",
38+
39+
path: number[],
40+
operation: integerOperation | booleanOperation |
41+
arrayOperation | objectOperation;
42+
}

backend/editor/OT/client_view.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ import (
1414
type clientView struct {
1515
socket *websocket.Conn
1616

17-
sendOp chan data.OperationRequest
17+
sendOp chan data.Operation
1818
sendAcknowledgement chan empty
1919
sendTerminateSignal chan empty
2020
}
2121

2222
func newClient(socket *websocket.Conn) *clientView {
2323
return &clientView{
2424
socket: socket,
25-
sendOp: make(chan data.OperationRequest),
25+
sendOp: make(chan data.Operation),
2626
sendAcknowledgement: make(chan empty),
2727
sendTerminateSignal: make(chan empty),
2828
}
@@ -52,7 +52,7 @@ func (c *clientView) run(serverPipe pipe, terminatePipe alertLeaving) {
5252
return
5353

5454
default:
55-
request := data.OperationRequest{}
55+
request := data.Operation{}
5656

5757
err := c.socket.ReadJSON(&request)
5858
if err != nil {

backend/editor/OT/data/applicator.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,20 @@ package data
33
// applicator manages the application of request objects to a provided datamodel
44

55
import (
6-
"errors"
76
"reflect"
87

98
"cms.csesoc.unsw.edu.au/editor/OT/data/datamodels"
109
)
1110

1211
// ApplyRequest takes a datamodel (as defined in the datamodels folder) and a request, it then proceeds to apply the request
1312
// to the model, note that this assumes that the operation in the request has been appropriately transformed
14-
func ApplyRequest(model datamodels.DataModel, request OperationRequest) error {
13+
func ApplyRequest(model datamodels.DataModel, request Operation) error {
1514
// TODO: Use Gary's code here to get the indice path
1615
_, err := GetOperationTargetSite(model, []int{})
1716
if err != nil {
1817
return err
1918
}
2019

21-
switch editType := request.OperationPayload.GetType(); editType {
22-
case TextEditType:
23-
case KeyEditType:
24-
case ArrayEditType:
25-
default:
26-
return errors.New("invalid edit type")
27-
}
28-
2920
return nil
3021
}
3122

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package data
2+
3+
import (
4+
"cms.csesoc.unsw.edu.au/editor/OT/data/datamodels"
5+
"cms.csesoc.unsw.edu.au/pkg/cmsjson"
6+
)
7+
8+
// ArrayOperation is an operation on an array type
9+
// @implements OperationModel
10+
type ArrayOperation struct {
11+
index int
12+
payload datamodels.DataType
13+
}
14+
15+
// TransformAgainst is the ArrayOperation implementation of the operationModel interface
16+
func (arrOp ArrayOperation) TransformAgainst(operation OperationModel) (OperationModel, OperationModel) {
17+
return arrOp, operation
18+
}
19+
20+
// Apply is the ArrayOperation implementation of the OperationModel interface, it does nothing
21+
func (arrOp ArrayOperation) Apply(ast cmsjson.AstNode) cmsjson.AstNode {
22+
return ast
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package data
2+
3+
import "cms.csesoc.unsw.edu.au/pkg/cmsjson"
4+
5+
// BooleanOperations represents an operation on a boolean type
6+
// @implements OperationModel
7+
type BooleanOperation struct {
8+
newValue bool
9+
}
10+
11+
// TransformAgainst is the BooleanOperation implementation of the operationModel interface
12+
func (boolOp BooleanOperation) TransformAgainst(operation OperationModel) (OperationModel, OperationModel) {
13+
return boolOp, operation
14+
}
15+
16+
// Apply is the BooleanOperation implementation of the OperationModel interface, it does nothing
17+
func (boolOp BooleanOperation) Apply(ast cmsjson.AstNode) cmsjson.AstNode {
18+
return ast
19+
}

backend/editor/OT/data/datamodels/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ package datamodels
66
type DataModel interface {
77
IsExposed() bool
88
}
9+
10+
// DataType is a series of types that can be used within your datamodel, they require registration in the
11+
// json configuration file
12+
type DataType interface{}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package data
2+
3+
import "cms.csesoc.unsw.edu.au/pkg/cmsjson"
4+
5+
// IntegerOperation represents an operation on an integer type
6+
// @implementations of OperationModel
7+
type IntegerOperation struct {
8+
newValue int
9+
}
10+
11+
// TransformAgainst is the IntegerOperation implementation of the operationModel interface
12+
func (intOp IntegerOperation) TransformAgainst(operation OperationModel) (OperationModel, OperationModel) {
13+
return intOp, operation
14+
}
15+
16+
// Apply is the IntegerOperation implementation of the OperationModel interface, it does nothing
17+
func (intOp IntegerOperation) Apply(ast cmsjson.AstNode) cmsjson.AstNode {
18+
return ast
19+
}
Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package data
22

33
import (
4-
"errors"
54
"reflect"
6-
"strconv"
75

86
"cms.csesoc.unsw.edu.au/editor/OT/data/datamodels/cmsmodel"
97
"cms.csesoc.unsw.edu.au/pkg/cmsjson"
@@ -19,33 +17,20 @@ var cmsJsonConf = cmsjson.Configuration{
1917
// Registration for cmsmodel, when the LP is finally merged with CSESoc Projects
2018
// this will also contain the registration for their data models
2119
RegisteredTypes: map[reflect.Type]map[string]reflect.Type{
20+
// TODO: later allow this to be dynamically swapped out for different front-ends
2221
reflect.TypeOf((*cmsmodel.Component)(nil)).Elem(): {
2322
"image": reflect.TypeOf(cmsmodel.Image{}),
2423
"paragraph": reflect.TypeOf(cmsmodel.Paragraph{}),
2524
},
2625

27-
reflect.TypeOf((*Payload)(nil)).Elem(): {
28-
"textEdit": reflect.TypeOf(TextEdit{}),
29-
"keyEdit": reflect.TypeOf(KeyEdit{}),
30-
"arrayEdit": reflect.TypeOf(ArrayEdit{}),
26+
// Type registrations for the OperationModel
27+
reflect.TypeOf((*OperationModel)(nil)).Elem(): {
28+
"integerOperation": reflect.TypeOf(IntegerOperation{}),
29+
"booleanOperation": reflect.TypeOf(BooleanOperation{}),
30+
"stringOperation": reflect.TypeOf(StringOperation{}),
31+
32+
"arrayOperation": reflect.TypeOf(ArrayOperation{}),
33+
"objectOperation": reflect.TypeOf(ObjectOperation{}),
3134
},
3235
},
3336
}
34-
35-
// small helper function to parse a JSON value of a specific type
36-
func parseDataGivenType(dataStr string, dataType string) (interface{}, error) {
37-
switch dataType {
38-
case "integer":
39-
return strconv.Atoi(dataStr)
40-
case "boolean":
41-
return strconv.ParseBool(dataStr)
42-
case "float":
43-
return strconv.ParseFloat(dataStr, 64)
44-
case "string":
45-
return dataStr, nil
46-
case "component":
47-
// todo: later
48-
return nil, nil
49-
}
50-
return nil, errors.New("unable to parse data type")
51-
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package data
2+
3+
import "cms.csesoc.unsw.edu.au/pkg/cmsjson"
4+
5+
// Noop represents a non-existent operation
6+
// @implements OperationModel
7+
type Noop struct{}
8+
9+
// TransformAgainst is the noop implementation of the operationModel interface
10+
func (noop Noop) TransformAgainst(operation OperationModel) (OperationModel, OperationModel) {
11+
return noop, operation
12+
}
13+
14+
// Apply is the noop implementation of the OperationModel interface, it does nothing
15+
func (noop Noop) Apply(ast cmsjson.AstNode) cmsjson.AstNode {
16+
return ast
17+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package data
2+
3+
import (
4+
"cms.csesoc.unsw.edu.au/editor/OT/data/datamodels"
5+
"cms.csesoc.unsw.edu.au/pkg/cmsjson"
6+
)
7+
8+
// ObjectOperation represents an operation we perform on an object
9+
type ObjectOperation struct {
10+
payload datamodels.DataType
11+
}
12+
13+
// TransformAgainst is the ArrayOperation implementation of the operationModel interface
14+
func (objOp ObjectOperation) TransformAgainst(operation OperationModel) (OperationModel, OperationModel) {
15+
return objOp, operation
16+
}
17+
18+
// Apply is the ArrayOperation implementation of the OperationModel interface, it does nothing
19+
func (objOp ObjectOperation) Apply(ast cmsjson.AstNode) cmsjson.AstNode {
20+
return ast
21+
}

backend/editor/OT/data/operationModel.go

Lines changed: 0 additions & 84 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package data
2+
3+
import (
4+
"errors"
5+
6+
"cms.csesoc.unsw.edu.au/pkg/cmsjson"
7+
)
8+
9+
type (
10+
// EditType is the underlying type of an edit
11+
EditType int
12+
13+
// OperationModel defines an simple interface an operation must implement
14+
OperationModel interface {
15+
TransformAgainst(OperationModel) (OperationModel, OperationModel)
16+
Apply(cmsjson.AstNode) cmsjson.AstNode
17+
}
18+
19+
// Operation is the fundamental incoming type from the frontend
20+
Operation struct {
21+
Path []int
22+
OperationType EditType
23+
AcknowledgedServerOps int
24+
25+
IsNoOp bool
26+
Operation OperationModel
27+
}
28+
)
29+
30+
// EditType enum constants
31+
const (
32+
Insert EditType = iota
33+
Delete
34+
)
35+
36+
// NoOperation is a special constant that signifies an operation that does nothing
37+
var NoOperation = Operation{IsNoOp: true, Operation: Noop{}}
38+
39+
// Parse is a utility function that takes a JSON stream and parses the input into
40+
// a Request object
41+
func ParseOperation(request string) (Operation, error) {
42+
if operation, err := cmsjson.Unmarshall[Operation](cmsJsonConf, []byte(request)); err != nil {
43+
return Operation{}, errors.New("invalid request format")
44+
} else {
45+
return *operation, nil
46+
}
47+
}

0 commit comments

Comments
 (0)