Light it up!
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54e1ced
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+profile.out
+cover.html
+coverage.txt
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a05ffb1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2016 Apigee Corporation
+
+Licensed 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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c7c1436
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# apidVerifyAPIKey
+
+This core plugin for [apid](http://github.com/30x/apid) responds to [apidApigeeSync](https://github.com/30x/apidApigeeSync) 
+events and publishes an API that allows clients to verify an API key against Apigee.
+
diff --git a/api.go b/api.go
new file mode 100644
index 0000000..241e472
--- /dev/null
+++ b/api.go
@@ -0,0 +1,167 @@
+package apidVerifyApiKey
+
+import (
+	"database/sql"
+	"encoding/json"
+	"fmt"
+	"net/http"
+)
+
+type sucResponseDetail struct {
+	Key             string `json:"key"`
+	ExpiresAt       int64  `json:"expiresAt"`
+	IssuedAt        int64  `json:"issuedAt"`
+	Status          string `json:"status"`
+	RedirectionURIs string `json:"redirectionURIs"`
+	DeveloperAppId  string `json:"developerId"`
+	DeveloperAppNam string `json:"developerAppName"`
+}
+
+type errResultDetail struct {
+	ErrorCode string `json:"errorCode"`
+	Reason    string `json:"reason"`
+}
+
+type kmsResponseSuccess struct {
+	RspInfo sucResponseDetail `json:"result"`
+	Type    string            `json:"type"`
+}
+
+type kmsResponseFail struct {
+	ErrInfo errResultDetail `json:"result"`
+	Type    string          `json:"type"`
+}
+
+// handle client API
+func handleRequest(w http.ResponseWriter, r *http.Request) {
+	if r.Method != "POST" {
+		return
+	}
+
+	err := r.ParseForm()
+	if err != nil {
+		w.WriteHeader(http.StatusBadRequest)
+		w.Write([]byte("Unable to parse form"))
+	}
+
+	f := r.Form
+	elems := []string{"action", "key", "uriPath", "organization", "environment"}
+	for _, elem := range elems {
+		if f.Get(elem) == "" {
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte(fmt.Sprintf("Missing element: %s", elem)))
+		}
+	}
+
+	org := f.Get("organization")
+	key := f.Get("key")
+	path := f.Get("uriPath")
+	env := f.Get("environment")
+	action := f.Get("action")
+
+	b, err := verifyAPIKey(key, path, env, org, action)
+	if err != nil {
+		log.Errorf("error: %s", err)
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write([]byte(err.Error()))
+		return
+	}
+
+	log.Debugf("handleVerifyAPIKey result %s", b)
+	w.Write(b)
+}
+
+// todo: The following was basically just copied from old APID - needs review.
+
+// returns []byte to be written to client
+func verifyAPIKey(key, path, env, org, action string) ([]byte, error) {
+	var (
+		sSql                               string
+		status, redirectionURIs            string
+		developerAppName, developerId      string
+		resName, resEnv, reason, errorCode string
+		issuedAt, expiresAt                int64
+	)
+
+	if key == "" || org == "" || path == "" || env == "" || action != "verify" {
+		log.Error("Input params Invalid/Incomplete")
+		reason = "Input Params Incomplete or Invalid"
+		errorCode = "INCORRECT_USER_INPUT"
+		return errorResponse(reason, errorCode)
+	}
+
+	db, err := data.DB()
+	if err != nil {
+		log.Errorf("Unable to access DB")
+		reason = err.Error()
+		errorCode = "SEARCH_INTERNAL_ERROR"
+		return errorResponse(reason, errorCode)
+	}
+
+	/*
+	 * NOTE: that here c.expired_at has been commented out because it is not
+	 * kept track of by Cassandra (hence always defaults to -1). FIXME
+	 */
+
+	sSql = "SELECT ap.res_names, ap.env, c.issued_at, c.status, a.cback_url, d.username, d.id FROM APP_CREDENTIAL AS c INNER JOIN APP AS a ON c.app_id = a.id INNER JOIN DEVELOPER AS d ON a.dev_id = d.id INNER JOIN APP_AND_API_PRODUCT_MAPPER as mp ON mp.app_cred_id = c.id INNER JOIN API_PRODUCT as ap ON ap.id = mp.api_prdt_id WHERE (UPPER(d.sts) = 'ACTIVE' AND mp.api_prdt_id = ap.id AND mp.app_id = a.id AND mp.app_cred_id = c.id AND UPPER(mp.api_prdt_status) = 'APPROVED' AND UPPER(a.status) = 'APPROVED' AND UPPER(c.status) = 'APPROVED' AND c.id = '" + key + "' AND c.org = '" + org + "');"
+
+	err = db.QueryRow(sSql).Scan(&resName, &resEnv, &issuedAt, &status,
+		&redirectionURIs, &developerAppName, &developerId)
+	expiresAt = -1
+	switch {
+	case err == sql.ErrNoRows:
+		reason = "API Key verify failed for (" + key + ", " + org + ", " + path + ", " + env + ")"
+		errorCode = "REQ_ENTRY_NOT_FOUND"
+		return errorResponse(reason, errorCode)
+
+	case err != nil:
+		reason = err.Error()
+		errorCode = "SEARCH_INTERNAL_ERROR"
+		return errorResponse(reason, errorCode)
+	}
+
+	/*
+	 * Perform all validations related to the Query made with the data
+	 * we just retrieved
+	 */
+	result := validatePath(resName, path)
+	if result == false {
+		reason = "Path Validation Failed (" + resName + " vs " + path + ")"
+		errorCode = "PATH_VALIDATION_FAILED"
+		return errorResponse(reason, errorCode)
+
+	}
+
+	/* Verify if the ENV matches */
+	result = validateEnv(resEnv, env)
+	if result == false {
+		reason = "ENV Validation Failed (" + resEnv + " vs " + env + ")"
+		errorCode = "ENV_VALIDATION_FAILED"
+		return errorResponse(reason, errorCode)
+	}
+
+	resp := kmsResponseSuccess{
+		Type: "APIKeyContext",
+		RspInfo: sucResponseDetail{
+			Key:             key,
+			ExpiresAt:       expiresAt,
+			IssuedAt:        issuedAt,
+			Status:          status,
+			RedirectionURIs: redirectionURIs,
+			DeveloperAppId:  developerId,
+			DeveloperAppNam: developerAppName},
+	}
+	return json.Marshal(resp)
+}
+
+func errorResponse(reason, errorCode string) ([]byte, error) {
+
+	log.Error(reason)
+	resp := kmsResponseFail{
+		Type: "ErrorResult",
+		ErrInfo: errResultDetail{
+			Reason:    reason,
+			ErrorCode: errorCode},
+	}
+	return json.Marshal(resp)
+}
diff --git a/api_test.go b/api_test.go
new file mode 100644
index 0000000..2d9d921
--- /dev/null
+++ b/api_test.go
@@ -0,0 +1,238 @@
+package apidVerifyApiKey
+
+import (
+	"database/sql"
+	"encoding/json"
+	"github.com/30x/apid"
+	"github.com/30x/apid/factory"
+	. "github.com/30x/apidApigeeSync" // for direct access to Payload types
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"os"
+	"strconv"
+	"strings"
+)
+
+var _ = Describe("api", func() {
+
+	var tmpDir string
+	var db *sql.DB
+	var server *httptest.Server
+
+	BeforeSuite(func() {
+		apid.Initialize(factory.DefaultServicesFactory())
+
+		config := apid.Config()
+
+		var err error
+		tmpDir, err = ioutil.TempDir("", "api_test")
+		Expect(err).NotTo(HaveOccurred())
+
+		config.Set("data_path", tmpDir)
+
+		// init() will create the tables
+		apid.InitializePlugins()
+
+		db, err = apid.Data().DB()
+		Expect(err).NotTo(HaveOccurred())
+		insertTestData(db)
+
+		server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+			if req.URL.Path == apiPath {
+				handleRequest(w, req)
+			}
+		}))
+	})
+
+	AfterSuite(func() {
+		apid.Events().Close()
+		server.Close()
+		os.RemoveAll(tmpDir)
+	})
+
+	Context("verifyAPIKey() directly", func() {
+
+		It("should reject a bad key", func() {
+			rsp, err := verifyAPIKey("credential_x", "/test", "Env_0", "Org_0", "verify")
+			Expect(err).ShouldNot(HaveOccurred())
+
+			var respj kmsResponseFail
+			json.Unmarshal(rsp, &respj)
+			Expect(respj.Type).Should(Equal("ErrorResult"))
+			Expect(respj.ErrInfo.ErrorCode).Should(Equal("REQ_ENTRY_NOT_FOUND"))
+
+		})
+
+		It("should reject a key once it's deleted", func() {
+			pd0 := &DataPayload{
+				EntityIdentifier: "credential_0",
+			}
+			res := deleteCredential(*pd0, db, "Org_0")
+			Expect(res).Should(BeTrue())
+
+			var respj kmsResponseFail
+			rsp, err := verifyAPIKey("credential_0", "/test", "Env_0", "Org_0", "verify")
+			Expect(err).ShouldNot(HaveOccurred())
+
+			json.Unmarshal(rsp, &respj)
+			Expect(respj.Type).Should(Equal("ErrorResult"))
+			Expect(respj.ErrInfo.ErrorCode).Should(Equal("REQ_ENTRY_NOT_FOUND"))
+		})
+
+		It("should successfully verify good keys", func() {
+			for i := 1; i < 10; i++ {
+				resulti := strconv.FormatInt(int64(i), 10)
+				rsp, err := verifyAPIKey("credential_"+resulti, "/test", "Env_0", "Org_0", "verify")
+				Expect(err).ShouldNot(HaveOccurred())
+
+				var respj kmsResponseSuccess
+				json.Unmarshal(rsp, &respj)
+				Expect(respj.Type).Should(Equal("APIKeyContext"))
+				Expect(respj.RspInfo.Key).Should(Equal("credential_" + resulti))
+			}
+		})
+	})
+
+	Context("access via API", func() {
+
+		It("should reject a bad key", func() {
+
+			uri, err := url.Parse(server.URL)
+			uri.Path = apiPath
+
+			v := url.Values{}
+			v.Add("organization", "Org_0")
+			v.Add("key", "credential_x")
+			v.Add("environment", "Env_0")
+			v.Add("uriPath", "/test")
+			v.Add("action", "verify")
+
+			client := &http.Client{}
+			req, err := http.NewRequest("POST", uri.String(), strings.NewReader(v.Encode()))
+			req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
+
+			res, err := client.Do(req)
+			defer res.Body.Close()
+			Expect(err).ShouldNot(HaveOccurred())
+
+			var respj kmsResponseFail
+			body, err := ioutil.ReadAll(res.Body)
+			Expect(err).ShouldNot(HaveOccurred())
+			json.Unmarshal(body, &respj)
+			Expect(respj.Type).Should(Equal("ErrorResult"))
+			Expect(respj.ErrInfo.ErrorCode).Should(Equal("REQ_ENTRY_NOT_FOUND"))
+		})
+
+		It("should successfully verify a good key", func() {
+
+			uri, err := url.Parse(server.URL)
+			uri.Path = apiPath
+
+			v := url.Values{}
+			v.Add("organization", "Org_0")
+			v.Add("key", "credential_1")
+			v.Add("environment", "Env_0")
+			v.Add("uriPath", "/test")
+			v.Add("action", "verify")
+
+			client := &http.Client{}
+			req, err := http.NewRequest("POST", uri.String(), strings.NewReader(v.Encode()))
+			req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
+
+			res, err := client.Do(req)
+			defer res.Body.Close()
+			Expect(err).ShouldNot(HaveOccurred())
+
+			var respj kmsResponseSuccess
+			body, err := ioutil.ReadAll(res.Body)
+			Expect(err).ShouldNot(HaveOccurred())
+			json.Unmarshal(body, &respj)
+			Expect(respj.Type).Should(Equal("APIKeyContext"))
+			Expect(respj.RspInfo.Key).Should(Equal("credential_1"))
+		})
+	})
+})
+
+func insertTestData(db *sql.DB) {
+
+	for i := 0; i < 10; i++ {
+		result := strconv.FormatInt(int64(i), 10)
+		pd0 := &DataPayload{
+			PldCont: Payload{
+				AppName:      "Api_product_" + result,
+				Resources:    []string{"/**", "/test"},
+				Environments: []string{"Env_0", "Env_1"},
+			},
+		}
+
+		res := insertAPIproduct(*pd0, db, "Org_0")
+		Expect(res).Should(BeTrue())
+	}
+
+	for i := 0; i < 10; i++ {
+		result := strconv.FormatInt(int64(i), 10)
+
+		pd1 := &DataPayload{
+			EntityIdentifier: "developer_id_" + result,
+			PldCont: Payload{
+				Email:     "person_0@apigee.com",
+				Status:    "Active",
+				UserName:  "user_0",
+				FirstName: "user_first_name0",
+				LastName:  "user_last_name0",
+			},
+		}
+
+		res := insertCreateDeveloper(*pd1, db, "Org_0")
+		Expect(res).Should(BeTrue())
+	}
+
+	var j, k int
+	for i := 0; i < 10; i++ {
+		resulti := strconv.FormatInt(int64(i), 10)
+		for j = k; j < 10+k; j++ {
+			resultj := strconv.FormatInt(int64(j), 10)
+			pd2 := &DataPayload{
+				EntityIdentifier: "application_id_" + resultj,
+				PldCont: Payload{
+					Email:       "person_0@apigee.com",
+					Status:      "Approved",
+					AppName:     "application_id_" + resultj,
+					DeveloperId: "developer_id_" + resulti,
+					CallbackUrl: "call_back_url_0",
+				},
+			}
+
+			res := insertCreateApplication(*pd2, db, "Org_0")
+			Expect(res).Should(BeTrue())
+		}
+		k = j
+	}
+
+	j = 0
+	k = 0
+	for i := 0; i < 10; i++ {
+		resulti := strconv.FormatInt(int64(i), 10)
+		for j = k; j < 10+k; j++ {
+			resultj := strconv.FormatInt(int64(j), 10)
+			pd3 := &DataPayload{
+				EntityIdentifier: "credential_" + resultj,
+				PldCont: Payload{
+					AppId:          "application_id_" + resulti,
+					Status:         "Approved",
+					ConsumerSecret: "consumer_secret_0",
+					IssuedAt:       349583485,
+					ApiProducts:    []Apip{{ApiProduct: "Api_product_0", Status: "Approved"}},
+				},
+			}
+
+			res := insertCreateCredential(*pd3, db, "Org_0")
+			Expect(res).Should(BeTrue())
+		}
+		k = j
+	}
+}
diff --git a/cover.sh b/cover.sh
new file mode 100755
index 0000000..7c312e5
--- /dev/null
+++ b/cover.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -e
+echo "mode: atomic" > coverage.txt
+
+for d in $(go list ./... | grep -v vendor); do
+    go test -coverprofile=profile.out -covermode=atomic $d
+    if [ -f profile.out ]; then
+        tail +2 profile.out >> coverage.txt
+        rm profile.out
+    fi
+done
+go tool cover -html=coverage.txt -o cover.html
diff --git a/glide.yaml b/glide.yaml
new file mode 100644
index 0000000..2dcac69
--- /dev/null
+++ b/glide.yaml
@@ -0,0 +1,7 @@
+package: github.com/30x/apidVerifyAPIKey
+import:
+- package: github.com/30x/apid
+- package: github.com/30x/apidApigeeSync
+testImport:
+- package: github.com/onsi/ginkgo/ginkgo
+- package: github.com/onsi/gomega
\ No newline at end of file
diff --git a/init.go b/init.go
new file mode 100644
index 0000000..a8dd899
--- /dev/null
+++ b/init.go
@@ -0,0 +1,57 @@
+package apidVerifyApiKey
+
+import (
+	"database/sql"
+	"github.com/30x/apid"
+	"github.com/30x/apidApigeeSync"
+)
+
+const (
+	apiPath = "/verifyAPIKey"
+)
+
+var (
+	log    apid.LogService
+	data   apid.DataService
+	events apid.EventsService
+)
+
+func init() {
+	apid.RegisterPlugin(initPlugin)
+}
+
+func initPlugin(services apid.Services) error {
+	log = services.Log().ForModule("apidVerifyAPIKey")
+	log.Debug("start init")
+
+	data = services.Data()
+	events = services.Events()
+
+	db, err := data.DB()
+	if err != nil {
+		log.Panic("Unable to access DB", err)
+	}
+
+	var count int
+	row := db.QueryRow("SELECT count(*) FROM sqlite_master WHERE type='table';")
+	if err := row.Scan(&count); err != nil {
+		log.Panic("Unable to setup database", err)
+	}
+	if count == 0 {
+		createTables(db)
+	}
+
+	services.API().HandleFunc(apiPath, handleRequest)
+
+	events.Listen(apidApigeeSync.ApigeeSyncEventSelector, &handler{})
+	log.Debug("end init")
+
+	return nil
+}
+
+func createTables(db *sql.DB) {
+	_, err := db.Exec("CREATE TABLE COMPANY (org varchar(255), id varchar(255), PRIMARY KEY (id, org));CREATE TABLE DEVELOPER (org varchar(255), email varchar(255), id varchar(255), sts varchar(255), username varchar(255), firstname varchar(255), lastname varchar(255), apigee_scope varchar(255), enc_password varchar(255), salt varchar(255), created_at integer, created_by varchar(255), updated_at integer, updated_by varchar(255), PRIMARY KEY (id, org));CREATE TABLE APP (org varchar(255), id varchar(255), dev_id varchar(255) null, cmp_id varchar(255) null, display_name varchar(255), apigee_scope varchar(255), type varchar(255), access_type varchar(255), cback_url varchar(255), status varchar(255), name varchar(255), app_family varchar(255), created_at integer, created_by varchar(255), updated_at integer, updated_by varchar(255), PRIMARY KEY (id, org), FOREIGN KEY (dev_id, org) references DEVELOPER (id, org) ON DELETE CASCADE);CREATE TABLE APP_CREDENTIAL (org varchar(255), id varchar(255), app_id varchar(255), cons_secret varchar(255), method_type varchar(255), status varchar(255), issued_at integer, expire_at integer, created_at integer, created_by varchar(255), updated_at integer, updated_by varchar(255), PRIMARY KEY (id, org), FOREIGN KEY (app_id, org) references app (id, org) ON DELETE CASCADE);CREATE TABLE API_PRODUCT (org varchar(255), id varchar(255), res_names varchar(255), env varchar(255), PRIMARY KEY (id, org));CREATE TABLE COMPANY_DEVELOPER (org varchar(255), dev_id varchar(255), id varchar(255), cmpny_id varchar(255), PRIMARY KEY (id, org), FOREIGN KEY (cmpny_id) references company(id) ON DELETE CASCADE, FOREIGN KEY (dev_id, org) references DEVELOPER(id, org) ON DELETE CASCADE);CREATE TABLE APP_AND_API_PRODUCT_MAPPER (org varchar(255), api_prdt_id varchar(255), app_id varchar(255), app_cred_id varchar(255), api_prdt_status varchar(255), PRIMARY KEY (org, api_prdt_id, app_id, app_cred_id), FOREIGN KEY (api_prdt_id, org) references api_product(id, org) ON DELETE CASCADE, FOREIGN KEY (app_cred_id, org) references app_credential(id, org) ON DELETE CASCADE, FOREIGN KEY (app_id, org) references app(id, org) ON DELETE CASCADE);")
+	if err != nil {
+		log.Panic("Unable to initialize DB", err)
+	}
+}
diff --git a/listener.go b/listener.go
new file mode 100644
index 0000000..6aec26f
--- /dev/null
+++ b/listener.go
@@ -0,0 +1,254 @@
+package apidVerifyApiKey
+
+import (
+	"database/sql"
+	"encoding/json"
+	"github.com/30x/apid"
+	"github.com/30x/apidApigeeSync"
+)
+
+type handler struct {
+}
+
+func (h *handler) String() string {
+	return "verifyAPIKey"
+}
+
+// todo: The following was basically just copied from old APID - needs review.
+
+func (h *handler) Handle(e apid.Event) {
+	changeSet, ok := e.(*apidApigeeSync.ChangeSet)
+	if !ok {
+		log.Errorf("Received non-ChangeSet event. This shouldn't happen!")
+		return
+	}
+
+	log.Debugf("apigeeSyncEvent: %d changes", len(changeSet.Changes))
+
+	db, err := data.DB()
+	if err != nil {
+		panic("help me!") // todo: handle
+	}
+
+	for _, payload := range changeSet.Changes {
+
+		org := payload.Data.PldCont.Organization
+
+		switch payload.Data.EntityType {
+		case "developer":
+			switch payload.Data.Operation {
+			case "create":
+				insertCreateDeveloper(payload.Data, db, org)
+			}
+
+		case "app":
+			switch payload.Data.Operation {
+			case "create":
+				insertCreateApplication(payload.Data, db, org)
+			}
+
+		case "credential":
+			switch payload.Data.Operation {
+			case "create":
+				insertCreateCredential(payload.Data, db, org)
+
+			case "delete":
+				deleteCredential(payload.Data, db, org)
+			}
+
+		case "apiproduct":
+			switch payload.Data.Operation {
+			case "create":
+				insertAPIproduct(payload.Data, db, org)
+			}
+		}
+
+	}
+}
+
+/*
+ * INSERT INTO APP_CREDENTIAL op
+ */
+func insertCreateCredential(ele apidApigeeSync.DataPayload, db *sql.DB, org string) bool {
+
+	txn, _ := db.Begin()
+	isPass := true
+	_, err := txn.Exec("INSERT INTO APP_CREDENTIAL (org, id, app_id, cons_secret, status, issued_at)VALUES(?,?,?,?,?,?);",
+		org,
+		ele.EntityIdentifier,
+		ele.PldCont.AppId,
+		ele.PldCont.ConsumerSecret,
+		ele.PldCont.Status,
+		ele.PldCont.IssuedAt)
+
+	if err != nil {
+		isPass = false
+		log.Error("INSERT CRED Failed: ", ele.EntityIdentifier, org, ")", err)
+		goto OT
+	} else {
+		log.Info("INSERT CRED Success: (", ele.EntityIdentifier, org, ")")
+	}
+
+	/*
+	 * If the credentials has been successfully inserted, insert the
+	 * mapping entries associated with the credential
+	 */
+
+	for _, elem := range ele.PldCont.ApiProducts {
+
+		_, err = txn.Exec("INSERT INTO APP_AND_API_PRODUCT_MAPPER (org, api_prdt_id, app_id, app_cred_id, api_prdt_status) VALUES(?,?,?,?,?);",
+			org,
+			elem.ApiProduct,
+			ele.PldCont.AppId,
+			ele.EntityIdentifier,
+			elem.Status)
+
+		if err != nil {
+			isPass = false
+			log.Error("INSERT APP_AND_API_PRODUCT_MAPPER Failed: (",
+				org,
+				elem.ApiProduct,
+				ele.PldCont.AppId,
+				ele.EntityIdentifier,
+				")",
+				err)
+			break
+		} else {
+			log.Info("INSERT APP_AND_API_PRODUCT_MAPPER Success: (",
+				org,
+				elem.ApiProduct,
+				ele.PldCont.AppId,
+				ele.EntityIdentifier,
+				")")
+		}
+	}
+OT:
+	if isPass == true {
+		txn.Commit()
+	} else {
+		txn.Rollback()
+	}
+	return isPass
+
+}
+
+/*
+ * DELETE CRED
+ */
+func deleteCredential(ele apidApigeeSync.DataPayload, db *sql.DB, org string) bool {
+
+	txn, _ := db.Begin()
+
+	_, err := txn.Exec("DELETE FROM APP_CREDENTIAL WHERE org=? AND id=?;", org, ele.EntityIdentifier)
+
+	if err != nil {
+		log.Error("DELETE CRED Failed: (", ele.EntityIdentifier, org, ")", err)
+		txn.Rollback()
+		return false
+	} else {
+		log.Info("DELETE CRED Success: (", ele.EntityIdentifier, org, ")")
+		txn.Commit()
+		return true
+	}
+
+}
+
+/*
+ * Helper function to convert string slice in to JSON format
+ */
+func convertSlicetoStringFormat(inpslice []string) string {
+
+	bytes, _ := json.Marshal(inpslice)
+	return string(bytes)
+}
+
+/*
+ * INSERT INTO API product op
+ */
+func insertAPIproduct(ele apidApigeeSync.DataPayload, db *sql.DB, org string) bool {
+
+	txn, _ := db.Begin()
+	restr := convertSlicetoStringFormat(ele.PldCont.Resources)
+	envstr := convertSlicetoStringFormat(ele.PldCont.Environments)
+
+	_, err := txn.Exec("INSERT INTO API_PRODUCT (org, id, res_names, env) VALUES(?,?,?,?)",
+		org,
+		ele.PldCont.AppName,
+		restr,
+		envstr)
+
+	if err != nil {
+		log.Error("INSERT API_PRODUCT Failed: (", ele.PldCont.AppName, org, ")", err)
+		txn.Rollback()
+		return false
+	} else {
+		log.Info("INSERT API_PRODUCT Success: (", ele.PldCont.AppName, org, ")")
+		txn.Commit()
+		return true
+	}
+
+}
+
+/*
+ * INSERT INTO APP op
+ */
+func insertCreateApplication(ele apidApigeeSync.DataPayload, db *sql.DB, org string) bool {
+
+	txn, _ := db.Begin()
+
+	_, err := txn.Exec("INSERT INTO APP (org, id, dev_id,cback_url,status, name, app_family, created_at, created_by,updated_at, updated_by) VALUES(?,?,?,?,?,?,?,?,?,?,?);",
+		org,
+		ele.EntityIdentifier,
+		ele.PldCont.DeveloperId,
+		ele.PldCont.CallbackUrl,
+		ele.PldCont.Status,
+		ele.PldCont.AppName,
+		ele.PldCont.AppFamily,
+		ele.PldCont.CreatedAt,
+		ele.PldCont.CreatedBy,
+		ele.PldCont.LastModifiedAt,
+		ele.PldCont.LastModifiedBy)
+
+	if err != nil {
+		log.Error("INSERT APP Failed: (", ele.EntityIdentifier, org, ")", err)
+		txn.Rollback()
+		return false
+	} else {
+		log.Info("INSERT APP Success: (", ele.EntityIdentifier, org, ")")
+		txn.Commit()
+		return true
+	}
+
+}
+
+/*
+ * INSERT INTO DEVELOPER op
+ */
+func insertCreateDeveloper(ele apidApigeeSync.DataPayload, db *sql.DB, org string) bool {
+
+	txn, _ := db.Begin()
+
+	_, err := txn.Exec("INSERT INTO DEVELOPER (org, email, id, sts, username, firstname, lastname, created_at,created_by, updated_at, updated_by) VALUES(?,?,?,?,?,?,?,?,?,?,?);",
+		org,
+		ele.PldCont.Email,
+		ele.EntityIdentifier,
+		ele.PldCont.Status,
+		ele.PldCont.UserName,
+		ele.PldCont.FirstName,
+		ele.PldCont.LastName,
+		ele.PldCont.CreatedAt,
+		ele.PldCont.CreatedBy,
+		ele.PldCont.LastModifiedAt,
+		ele.PldCont.LastModifiedBy)
+
+	if err != nil {
+		log.Error("INSERT DEVELOPER Failed: (", ele.PldCont.UserName, org, ")", err)
+		txn.Rollback()
+		return false
+	} else {
+		log.Info("INSERT DEVELOPER Success: (", ele.PldCont.UserName, org, ")")
+		txn.Commit()
+		return true
+	}
+
+}
diff --git a/listener_test.go b/listener_test.go
new file mode 100644
index 0000000..cb283d7
--- /dev/null
+++ b/listener_test.go
@@ -0,0 +1,121 @@
+package apidVerifyApiKey
+
+import (
+	"encoding/json"
+	"github.com/30x/apid"
+	. "github.com/30x/apidApigeeSync" // for direct access to Payload types
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("listener", func() {
+
+	It("should store data from ApigeeSync in the database", func(done Done) {
+
+		var event = ChangeSet{}
+		event.Changes = []ChangePayload{
+			{
+				Data: DataPayload{
+					EntityType: "apiproduct",
+					Operation: "create",
+					PldCont: Payload{
+						Organization: "test_org",
+						AppName:      "Api_product_sync",
+						Resources:    []string{"/**", "/test"},
+						Environments: []string{"Env_0", "Env_1"},
+					},
+				},
+			},
+			{
+				Data: DataPayload{
+					EntityType: "developer",
+					Operation: "create",
+					EntityIdentifier: "developer_id_sync",
+					PldCont: Payload{
+						Organization: "test_org",
+						Email:     "person_sync@apigee.com",
+						Status:    "Active",
+						UserName:  "user_sync",
+						FirstName: "user_first_name_sync",
+						LastName:  "user_last_name_sync",
+					},
+				},
+			},
+			{
+				Data: DataPayload{
+					EntityType: "app",
+					Operation: "create",
+					EntityIdentifier: "application_id_sync",
+					PldCont: Payload{
+						Organization: "test_org",
+						Email:       "person_sync@apigee.com",
+						Status:      "Approved",
+						AppName:     "application_id_sync",
+						DeveloperId: "developer_id_sync",
+						CallbackUrl: "call_back_url",
+					},
+				},
+			},
+			{
+				Data: DataPayload{
+					EntityType: "credential",
+					Operation: "create",
+					EntityIdentifier: "credential_sync",
+					PldCont: Payload{
+						Organization: "test_org",
+						AppId:          "application_id_sync",
+						Status:         "Approved",
+						ConsumerSecret: "consumer_secret_sync",
+						IssuedAt:       349583485,
+						ApiProducts:    []Apip{
+							{
+								ApiProduct: "Api_product_sync",
+								Status: "Approved",
+							},
+						},
+					},
+				},
+			},
+		}
+
+		h := &test_handler{
+			"checkDatabase",
+			func(e apid.Event) {
+
+				// ignore the first event, let standard listener process it
+				changeSet := e.(*ChangeSet)
+				if len(changeSet.Changes) > 0 {
+					return
+				}
+
+				rsp, err := verifyAPIKey("credential_sync", "/test", "Env_0", "test_org", "verify")
+				Expect(err).ShouldNot(HaveOccurred())
+
+				var respj kmsResponseSuccess
+				json.Unmarshal(rsp, &respj)
+				Expect(respj.Type).Should(Equal("APIKeyContext"))
+				Expect(respj.RspInfo.Key).Should(Equal("credential_sync"))
+
+				close(done)
+			},
+		}
+
+		apid.Events().Listen(ApigeeSyncEventSelector, h)
+		apid.Events().Emit(ApigeeSyncEventSelector, &event) // for standard listener
+		apid.Events().Emit(ApigeeSyncEventSelector, &ChangeSet{}) // for test listener
+	})
+
+})
+
+type test_handler struct {
+	description string
+	f           func(event apid.Event)
+}
+
+func (t *test_handler) String() string {
+	return t.description
+}
+
+func (t *test_handler) Handle(event apid.Event) {
+	t.f(event)
+}
diff --git a/validate_env.go b/validate_env.go
new file mode 100644
index 0000000..df0382d
--- /dev/null
+++ b/validate_env.go
@@ -0,0 +1,18 @@
+package apidVerifyApiKey
+
+import "encoding/json"
+
+/*
+ * Ensure the ENV matches.
+ */
+func validateEnv(envLocal, envInPath string) bool {
+
+	var ePaths []string
+	json.Unmarshal([]byte(envLocal), &ePaths)
+	for _, a := range ePaths {
+		if a == envInPath {
+			return true
+		}
+	}
+	return false
+}
diff --git a/validate_env_test.go b/validate_env_test.go
new file mode 100644
index 0000000..3942d7a
--- /dev/null
+++ b/validate_env_test.go
@@ -0,0 +1,26 @@
+package apidVerifyApiKey
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("Validate Env", func() {
+
+	It("validation1", func() {
+		s := validateEnv("[\"foo\",\"bar\"]", "foo")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation2", func() {
+		s := validateEnv("[\"foo\",\"bar\"]", "bar")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation3", func() {
+		s := validateEnv("[\"foo\",\"bar\"]", "xxx")
+		Expect(s).Should(BeFalse())
+	})
+	It("validation4", func() {
+		s := validateEnv("[]", "xxx")
+		Expect(s).Should(BeFalse())
+	})
+})
diff --git a/validate_path.go b/validate_path.go
new file mode 100644
index 0000000..f3cfb99
--- /dev/null
+++ b/validate_path.go
@@ -0,0 +1,45 @@
+package apidVerifyApiKey
+
+import (
+	"encoding/json"
+	"regexp"
+	"strings"
+)
+
+/*
+ * Check for the base path (API_Product) match with the path
+ * received in the Request, via the customized regex, where
+ * "**" gets de-normalized as ".*" and "*" as everything till
+ * the next "/".
+ */
+func validatePath(basePath, requestBase string) bool {
+
+	var basePaths []string
+	json.Unmarshal([]byte(basePath), &basePaths)
+	for _, a := range basePaths {
+		str1 := strings.Replace(a, "**", "(.*)", -1)
+		str2 := strings.Replace(a, "*", "([^/]+)", -1)
+		if a != str1 {
+			reg, _ := regexp.Compile(str1)
+			res := reg.MatchString(requestBase)
+			if res == true {
+				return true
+			}
+		} else if a != str2 {
+			reg, _ := regexp.Compile(str2)
+			res := reg.MatchString(requestBase)
+			if res == true {
+				return true
+			}
+		} else if requestBase == a {
+			return true
+		}
+
+		/*
+		 * FIXME: SINGLE_FORWARD_SLASH_PATTERN not supported yet
+		 */
+	}
+
+	/* if the i/p resource is empty, no checks need to be made */
+	return len(basePaths) == 0
+}
diff --git a/validate_path_test.go b/validate_path_test.go
new file mode 100644
index 0000000..0b76b07
--- /dev/null
+++ b/validate_path_test.go
@@ -0,0 +1,94 @@
+package apidVerifyApiKey
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("Validate Path", func() {
+
+	It("validation1", func() {
+		s := validatePath("", "/foo")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation2", func() {
+		s := validatePath("", "foo")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation3", func() {
+		s := validatePath("[]", "foo")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation4", func() {
+		s := validatePath("[\"/**\"]", "/foo")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation5", func() {
+		s := validatePath("[\"/**\"]", "foo")
+		Expect(s).Should(BeFalse())
+	})
+	It("validation6", func() {
+		s := validatePath("[\"/**\"]", "/")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation7", func() {
+		s := validatePath("[\"/foo/**\"]", "/")
+		Expect(s).Should(BeFalse())
+	})
+	It("validation8", func() {
+		s := validatePath("[\"/foo/**\"]", "/foo/")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation9", func() {
+		s := validatePath("[\"/foo/**\"]", "/foo/bar")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation10", func() {
+		s := validatePath("[\"/foo/**\"]", "foo")
+		Expect(s).Should(BeFalse())
+	})
+	It("validation11", func() {
+		s := validatePath("[\"/foo/bar/**\"]", "/foo/bar/xx/yy")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation12", func() {
+		s := validatePath("[\"/foo/bar/*\"]", "/foo/bar/xxx")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation13", func() {
+		s := validatePath("[\"/foo/bar/*/\"]", "/foo/bar/xxx")
+		Expect(s).Should(BeFalse())
+	})
+	It("validation14", func() {
+		s := validatePath("[\"/foo/bar/**\"]", "/foo/bar/xx/yy")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation15", func() {
+		s := validatePath("[\"/foo/*/**/\"]", "/foo/bar")
+		Expect(s).Should(BeFalse())
+	})
+	It("validation16", func() {
+		s := validatePath("[\"/foo/bar/*/xxx\"]", "/foo/bar/yyy/xxx")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation17", func() {
+		s := validatePath("[\"/foo/bar/*/xxx/\"]", "/foo/bar/yyy/xxx")
+		Expect(s).Should(BeFalse())
+	})
+	It("validation18", func() {
+		s := validatePath("[\"/foo/bar/**/xxx/\"]", "/foo/bar/aaa/bbb/xxx/")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation19", func() {
+		s := validatePath("[\"/foo/bar/***/xxx/\"]", "/foo/bar/aaa/bbb/xxx/")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation20", func() {
+		s := validatePath("[\"/foo/\", \"/bar/\"]", "/foo/")
+		Expect(s).Should(BeTrue())
+	})
+	It("validation21", func() {
+		s := validatePath("[\"/foo/bar/yy*/xxx\"]", "/foo/bar/yyy/xxx")
+		Expect(s).Should(BeTrue())
+	})
+})
diff --git a/verifyAPIKey_suite_test.go b/verifyAPIKey_suite_test.go
new file mode 100644
index 0000000..c8f47fe
--- /dev/null
+++ b/verifyAPIKey_suite_test.go
@@ -0,0 +1,13 @@
+package apidVerifyApiKey_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"testing"
+)
+
+func TestVerifyAPIKey(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "VerifyAPIKey Suite")
+}