feat: add mem object group & apisix model (#14)

* feat: add mem object group & apisix model

* merge go.mod
diff --git a/go.mod b/go.mod
index 023ec91..893e890 100644
--- a/go.mod
+++ b/go.mod
@@ -4,13 +4,13 @@
 
 require (
 	github.com/ghodss/yaml v1.0.0
+	github.com/gxthrj/seven v0.1.9
 	github.com/hashicorp/go-memdb v1.2.1
 	github.com/onsi/ginkgo v1.14.0
 	github.com/onsi/gomega v1.10.1
-	github.com/sergi/go-diff v1.1.0 // indirect
 	github.com/xeipuuv/gojsonschema v1.2.0
 	github.com/yudai/gojsondiff v1.0.0
-	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	go.uber.org/zap v1.15.0
+	gopkg.in/resty.v1 v1.12.0
 	gopkg.in/yaml.v2 v2.3.0
 )
diff --git a/go.sum b/go.sum
index b8fc16d..772ae66 100644
--- a/go.sum
+++ b/go.sum
@@ -7,6 +7,7 @@
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -19,8 +20,13 @@
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/gxthrj/apisix-types v0.1.0/go.mod h1:STdHATWxqIeJhUEXt6dZTd6z0To0qO70K/1mnfBtlC0=
+github.com/gxthrj/seven v0.1.9 h1:EccFVE5PIqZXyt91laXxGu9tydCjg31EXn/Q4aFfwD4=
+github.com/gxthrj/seven v0.1.9/go.mod h1:epDVCYT1ibfV6fgaeM918QAer9u0/M2LXW6OcIRdfQc=
+github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 github.com/hashicorp/go-immutable-radix v1.2.0 h1:l6UW37iCXwZkZoAbEYnptSHVE/cQ5bOTPYG5W3vf9+8=
 github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-memdb v1.0.4/go.mod h1:LWQ8R70vPrS4OEY9k28D2z8/Zzyu34NVzeRibGAzHO0=
 github.com/hashicorp/go-memdb v1.2.1 h1:wI9btDjYUOJJHTCnRlAG/TkRyD/ij7meJMrLK9X31Cc=
 github.com/hashicorp/go-memdb v1.2.1/go.mod h1:OSvLJ662Jim8hMM+gWGyhktyWk2xPCnWMc7DWIqtkGA=
 github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -32,13 +38,17 @@
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
 github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -61,6 +71,7 @@
 github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
 github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
 github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
+github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
 go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
@@ -73,6 +84,7 @@
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -82,6 +94,7 @@
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -112,6 +125,8 @@
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/pkg/adapter/route.go b/pkg/adapter/route.go
new file mode 100644
index 0000000..635573b
--- /dev/null
+++ b/pkg/adapter/route.go
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package adapter
+
+import (
+	"github.com/apache/apisix-control-plane/pkg/dp/apisix"
+	"github.com/apache/apisix-control-plane/pkg/mem"
+	"reflect"
+)
+
+func ToRoute(r *mem.Route) *apisix.Route {
+	return &apisix.Route{
+		Name:    r.FullName,
+		Hosts:   r.Hosts,
+		Desc:    r.Name,
+		Uris:    toUris(r),
+		Vars:    toVars(r),
+		Methods: r.Methods,
+	}
+}
+
+func toUris(r *mem.Route) []*string {
+	result := make([]*string, 0)
+	for _, m := range r.Match {
+		// uris
+		matchUris := m["uris"]
+		switch reflect.TypeOf(matchUris).Kind() {
+		case reflect.Slice:
+			s := reflect.ValueOf(matchUris)
+			if slice, ok := s.Interface().([]map[string]string); ok {
+				for _, s := range slice {
+					for k, v := range s {
+						switch k {
+						case "prefix":
+							uri := v + "*"
+							result = append(result, &uri)
+						case "exact":
+							result = append(result, &v)
+						}
+					}
+				}
+			}
+		}
+	}
+	return result
+}
+
+func toVars(r *mem.Route) [][]*string {
+	result := make([][]*string, 0)
+	for _, m := range r.Match {
+		// args
+		matchArgs := m["args"]
+		switch reflect.TypeOf(matchArgs).Kind() {
+		case reflect.Slice:
+			s := reflect.ValueOf(matchArgs)
+			if slice, ok := s.Interface().([]map[string]map[string]string); ok {
+				for _, s := range slice {
+					tmp := make([]*string, 0)
+					for p, value := range s {
+						tmp = append(tmp, &p)
+						for k, v := range value {
+							switch k {
+							case "greater":
+								greater := ">"
+								tmp = append(tmp, &greater)
+								tmp = append(tmp, &v)
+							case "exact":
+								equals := "=="
+								tmp = append(tmp, &equals)
+								tmp = append(tmp, &v)
+							}
+						}
+						result = append(result, tmp)
+					}
+
+				}
+			}
+		}
+	}
+	return result
+}
diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go
new file mode 100644
index 0000000..f56d629
--- /dev/null
+++ b/pkg/conf/conf.go
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package conf
+
+var (
+	ApiKey  = "edd1c9f034335f136f87ad84b625c8f1"
+	BaseUrl = "http://127.0.0.1:9080/apisix/admin"
+)
diff --git a/pkg/dp/apisix/model.go b/pkg/dp/apisix/model.go
new file mode 100644
index 0000000..093d18d
--- /dev/null
+++ b/pkg/dp/apisix/model.go
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package apisix
+
+type Route struct {
+	ID           *string     `json:"id,omitempty"`
+	Hosts        []*string   `json:"hosts,omitempty"`
+	Name         *string     `json:"name,omitempty"`
+	Desc         *string     `json:"desc,omitempty"`
+	Uris         []*string   `json:"uris"`
+	Vars         [][]*string `json:"vars,omitempty"`
+	Methods      []*string   `json:"methods,omitempty"`
+	ServiceId    *string     `json:"service_id,omitempty"`
+	ServiceName  *string     `json:"service_name,omitempty"`
+	UpstreamId   *string     `json:"upstream_id,omitempty"`
+	UpstreamName *string     `json:"upstream_name,omitempty"`
+	Plugins      []*Plugin   `json:"plugins,omitempty"`
+}
+
+type Plugin map[string]interface{}
+
+type Upstream struct {
+	ID              *string `json:"id,omitempty"`
+	Group           *string `json:"group,omitempty"`
+	ResourceVersion *string `json:"resource_version,omitempty"`
+	Name            *string `json:"name,omitempty"`
+	Type            *string `json:"type,omitempty"`
+	HashOn          *string `json:"hash_on,omitemtpy"`
+	Key             *string `json:"key,omitempty"`
+	Nodes           []*Node `json:"nodes,omitempty"`
+	FromKind        *string `json:"from_kind,omitempty"`
+}
+
+type Node struct {
+	IP     *string `json:"ip,omitempty"`
+	Port   *int    `json:"port,omitempty"`
+	Weight *int    `json:"weight,omitempty"`
+}
diff --git a/pkg/dp/apisix/route.go b/pkg/dp/apisix/route.go
new file mode 100644
index 0000000..bfa2a21
--- /dev/null
+++ b/pkg/dp/apisix/route.go
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package apisix
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/apache/apisix-control-plane/pkg/conf"
+	"github.com/apache/apisix-control-plane/pkg/utils"
+)
+
+func (route *Route) Add() (*RouteResponse, error) {
+	baseUrl := conf.BaseUrl
+	url := fmt.Sprintf("%s/routes", baseUrl)
+	if b, err := json.Marshal(route); err != nil {
+		return nil, err
+	} else {
+		if res, err := utils.Post(url, b); err != nil {
+			return nil, err
+		} else {
+			var routeResp RouteResponse
+			if err = json.Unmarshal(res, &routeResp); err != nil {
+				return nil, err
+			} else {
+				if routeResp.Route.Key != nil {
+					return &routeResp, nil
+				} else {
+					return nil, fmt.Errorf("apisix route not expected response")
+				}
+
+			}
+		}
+	}
+}
+
+func (route *Route) Update() (*RouteResponse, error) {
+	// todo
+	return nil, nil
+}
+
+func (route *Route) Delete() (*RouteResponse, error) {
+	// todo
+	return nil, nil
+}
+
+type RouteResponse struct {
+	Action string    `json:"action"`
+	Route  RouteNode `json:"node"`
+}
+
+type RouteNode struct {
+	Key   *string `json:"key"`
+	Value Value   `json:"value"`
+}
+
+type Value struct {
+	UpstreamId *string                `json:"upstream_id"`
+	ServiceId  *string                `json:"service_id"`
+	Plugins    map[string]interface{} `json:"plugins"`
+	Host       *string                `json:"host,omitempty"`
+	Uri        *string                `json:"uri"`
+	Desc       *string                `json:"desc"`
+	Methods    []*string              `json:"methods,omitempty"`
+}
diff --git a/pkg/dp/apisix/upstream.go b/pkg/dp/apisix/upstream.go
new file mode 100644
index 0000000..ce85e2e
--- /dev/null
+++ b/pkg/dp/apisix/upstream.go
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package apisix
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/apache/apisix-control-plane/pkg/utils"
+	"github.com/gxthrj/seven/conf"
+)
+
+func (upstream *Upstream) Add() (*UpstreamResponse, error) {
+	baseUrl := conf.BaseUrl
+	url := fmt.Sprintf("%s/upstreams", baseUrl)
+	if b, err := json.Marshal(upstream); err != nil {
+		return nil, err
+	} else {
+		if res, err := utils.Post(url, b); err != nil {
+			return nil, fmt.Errorf("http post failed, url: %s, err: %+v", url, err)
+		} else {
+			var uRes UpstreamResponse
+			if err = json.Unmarshal(res, &uRes); err != nil {
+				return nil, err
+			} else {
+				if uRes.Upstream.Key != nil {
+					return &uRes, nil
+				} else {
+					return nil, fmt.Errorf("apisix upstream not expected response")
+				}
+			}
+		}
+	}
+}
+
+func (upstream *Upstream) Update() (*UpstreamResponse, error) {
+	// todo
+	return nil, nil
+}
+
+func (upstream *Upstream) Delete() (*UpstreamResponse, error) {
+	// todo
+	return nil, nil
+}
+
+type UpstreamResponse struct {
+	Action   string       `json:"action"`
+	Upstream UpstreamNode `json:"node"`
+}
+
+type UpstreamNode struct {
+	Key           *string       `json:"key"`
+	UpstreamNodes UpstreamNodes `json:"value"`
+}
+
+type UpstreamNodes struct {
+	Nodes  map[string]int64 `json:"nodes"`
+	Desc   *string          `json:"desc"`
+	LBType *string          `json:"type"`
+}
diff --git a/pkg/mem/deps.go b/pkg/mem/deps.go
new file mode 100644
index 0000000..ed190e4
--- /dev/null
+++ b/pkg/mem/deps.go
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mem
+
+func Map(raws []MemModel) map[string][]MemModel {
+	result := make(map[string][]MemModel)
+	result[GatewayKind] = make([]MemModel, 0)
+	result[RouteKind] = make([]MemModel, 0)
+	result[UpstreamKind] = make([]MemModel, 0)
+	result[PluginKind] = make([]MemModel, 0)
+	for _, r := range raws {
+		switch v := r.(type) {
+		case *Gateway:
+			result[*v.Kind] = append(result[*v.Kind], v)
+		case *Route:
+			result[*v.Kind] = append(result[*v.Kind], v)
+		case *Upstream:
+			result[*v.Kind] = append(result[*v.Kind], v)
+		case *Plugin:
+			result[*v.Kind] = append(result[*v.Kind], v)
+		}
+	}
+	return result
+}
diff --git a/pkg/mem/model.go b/pkg/mem/model.go
index d82ba64..476cd1a 100644
--- a/pkg/mem/model.go
+++ b/pkg/mem/model.go
@@ -17,6 +17,11 @@
 
 package mem
 
+const GatewayKind = "Gateway"
+const RouteKind = "Route"
+const UpstreamKind = "Upstream"
+const PluginKind = "Plugin"
+
 type MemModel interface {
 	Diff(m MemModel) bool
 }
diff --git a/pkg/utils/http.go b/pkg/utils/http.go
new file mode 100644
index 0000000..45e5c17
--- /dev/null
+++ b/pkg/utils/http.go
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package utils
+
+import (
+	"fmt"
+	"net/http"
+	"time"
+
+	"gopkg.in/resty.v1"
+)
+
+const timeout = 3000
+
+func Post(url string, bytes []byte) ([]byte, error) {
+	r := resty.New().
+		SetTimeout(time.Duration(timeout)*time.Millisecond).
+		R().
+		SetHeader("content-type", "application/json")
+	r.SetBody(bytes)
+	resp, err := r.Post(url)
+	if err != nil {
+		return nil, err
+	}
+	if resp.StatusCode() != http.StatusOK && resp.StatusCode() != http.StatusCreated {
+		return nil, fmt.Errorf("status: %d, body: %s", resp.StatusCode(), resp.Body())
+	}
+	return resp.Body(), nil
+}
+
+func Patch(url string, bytes []byte) ([]byte, error) {
+	r := resty.New().
+		SetTimeout(time.Duration(timeout)*time.Millisecond).
+		R().
+		SetHeader("content-type", "application/json")
+	r.SetBody(bytes)
+	resp, err := r.Patch(url)
+	if err != nil {
+		return nil, err
+	}
+	if resp.StatusCode() != http.StatusOK {
+		return nil, fmt.Errorf("status: %d, body: %s", resp.StatusCode(), resp.Body())
+	}
+	return resp.Body(), nil
+}
+
+func Delete(url string) ([]byte, error) {
+	r := resty.New().
+		SetTimeout(time.Duration(timeout)*time.Millisecond).
+		R().
+		SetHeader("content-type", "application/json")
+	resp, err := r.Delete(url)
+	if err != nil {
+		return nil, err
+	}
+	if resp.StatusCode() != http.StatusOK {
+		return nil, fmt.Errorf("status: %d, body: %s", resp.StatusCode(), resp.Body())
+	}
+	return resp.Body(), nil
+}