func handleListObjectsWithFilters(orgID string, writer http.ResponseWriter, request *http.Request) {
	if trace.IsLogging(logger.DEBUG) {
		trace.Debug("In handleListObjectsWithFilters")
	}
	if pathParamValid := validatePathParam(writer, orgID, "", "", "", ""); !pathParamValid {
		return
	}
	code, userOrgID, userID := security.Authenticate(request)
	if code == security.AuthFailed || (code != security.AuthSyncAdmin && code != security.AuthAdmin && code != security.AuthUser && code != security.AuthNodeUser) {
		writer.WriteHeader(http.StatusForbidden)
		writer.Write(unauthorizedBytes)
		return
	}
	destinationPolicyString := request.URL.Query().Get("destinationPolicy")
	var destinationPolicy *bool
	if destinationPolicyString != "" {
		var err error
		destinationPolicyValue, err := strconv.ParseBool(destinationPolicyString)
		destinationPolicy = &destinationPolicyValue
		if err != nil {
			writer.WriteHeader(http.StatusBadRequest)
			return
		}
	}
	dpServiceOrgID := ""
	dpServiceName := ""
	dpPropertyName := ""
	since := int64(0)
	if destinationPolicy != nil && *destinationPolicy == true {
		dpServiceID := request.URL.Query().Get("dpService")
		if dpServiceID != "" {
			parts := strings.SplitN(dpServiceID, "/", 2)
			if len(parts) < 2 || len(parts[0]) == 0 || len(parts[1]) == 0 {
				writer.WriteHeader(http.StatusBadRequest)
				return
			}
			dpServiceOrgID = parts[0]
			dpServiceName = parts[1]
		}
		dpPropertyName = request.URL.Query().Get("dpPropertyName")
		if pathParamValid := validatePathParamForService(writer, dpServiceOrgID, dpServiceName, dpPropertyName); !pathParamValid {
			return
		}
		sinceString := request.URL.Query().Get("since")
		if sinceString != "" {
			var err error
			sinceTime, err := time.Parse(time.RFC3339, sinceString)
			if err != nil {
				writer.WriteHeader(http.StatusBadRequest)
				return
			}
			since = sinceTime.UTC().UnixNano()
			if since < 1 {
				writer.WriteHeader(http.StatusBadRequest)
				return
			}
		}
	}
	objectType := request.URL.Query().Get("objectType")
	objectID := ""
	if objectType != "" {
		objectID = request.URL.Query().Get("objectID")
	}
	destinationType := request.URL.Query().Get("destinationType")
	destinationID := ""
	if destinationType != "" {
		destinationID = request.URL.Query().Get("destinationID")
	}
	if pathParamValid := validatePathParam(writer, orgID, objectType, objectID, destinationType, destinationID); !pathParamValid {
		return
	}
	var noData *bool
	noDataString := request.URL.Query().Get("noData")
	if noDataString != "" {
		var err error
		noDataVelue, err := strconv.ParseBool(noDataString)
		noData = &noDataVelue
		if err != nil {
			writer.WriteHeader(http.StatusBadRequest)
			return
		}
	}
	expirationTimeBeforeString := request.URL.Query().Get("expirationTimeBefore")
	if expirationTimeBeforeString != "" {
		_, err := time.Parse(time.RFC3339, expirationTimeBeforeString)
		if err != nil {
			writer.WriteHeader(http.StatusBadRequest)
			return
		}
	}
	var deleted *bool
	deletedString := request.URL.Query().Get("deleted")
	if deletedString != "" {
		deletedValue, err := strconv.ParseBool(deletedString)
		deleted = &deletedValue
		if err != nil {
			writer.WriteHeader(http.StatusBadRequest)
			return
		}
	}
	var objects []common.MetaData
	var err error
	if trace.IsLogging(logger.DEBUG) {
		trace.Debug("In handleListObjectsWithFilters, get objects with %s %s %s %s %s %d %s %s %s %s %s %s %s\n", orgID, destinationPolicyString, dpServiceOrgID, dpServiceName, dpPropertyName, since, objectType, objectID, destinationType, destinationID, noDataString, expirationTimeBeforeString, deletedString)
	}
	if objects, err = ListObjectsWithFilters(orgID, destinationPolicy, dpServiceOrgID, dpServiceName, dpPropertyName, since, objectType, objectID, destinationType, destinationID, noData, expirationTimeBeforeString, deleted); err != nil {
		communications.SendErrorResponse(writer, err, "Failed to fetch the list of objects with given conditions. Error: ", 0)
	} else {
		if len(objects) == 0 {
			writer.WriteHeader(http.StatusNotFound)
		} else {
			if accessibleObjects, err := GetAccessibleObjects(code, orgID, userOrgID, userID, objects); err != nil {
				communications.SendErrorResponse(writer, err, "Failed to get accessible object. Error: ", 0)
			} else {
				if data, err := json.MarshalIndent(accessibleObjects, "", "  "); err != nil {
					communications.SendErrorResponse(writer, err, "Failed to marshal the list of objects with a Metadata. Error: ", 0)
				} else {
					writer.Header().Add(contentType, applicationJSON)
					writer.WriteHeader(http.StatusOK)
					if _, err := writer.Write(data); err != nil && log.IsLogging(logger.ERROR) {
						log.Error("Failed to write response body, error: " + err.Error())
					}
				}
			}
		}
	}
}
swagger:operation GET /api/v1/objects/{orgID}?filters=true handleListObjectsWithFilters

Get objects satisfy the given filters

Get the list of objects that satisfy the given filters
This is a CSS only API.

---

tags:
- CSS

produces:
- application/json
- text/plain

parameters:
- name: orgID
in: path
description: The orgID of the updated objects to return. Present only when working with a CSS, removed from the path when working with an ESS
required: true
type: string
- name: filters
in: query
description: Must be true to indicate that objects with filters are to be retrieved
required: true
type: boolean
- name: destinationPolicy
in: query
description: Must be true to indicate that objects with destinationPolicy are to be retrieved
required: false
type: boolean
- name: dpService
in: query
description: The ID of the service (orgID/serviceName) to which objects have affinity,
whose Destination Policy should be fetched.
required: false
type: string
- name: dpPropertyName
in: query
description: The property name defined inside destination policy to which objects have affinity,
whose Destination Policy should be fetched.
required: false
type: string
- name: since
in: query
description: Objects that have a Destination Policy which was updated since the specified timestamp in RFC3339 should be fetched.
required: false
type: string
- name: objectType
in: query
description: Fetch the objects with given object type
required: false
type: string
- name: objectID
in: query
description: Fetch the objects with given object id
required: false
type: string
- name: destinationType
in: query
description: Fetch the objects with given destination type
required: false
type: string
- name: destinationID
in: query
description: Fetch the objects with given destination id
required: false
type: string
- name: noData
in: query
description: Specify true or false. Fetch the objects with noData marked to true/false. If not specified, object will not be filtered by "noData" field
required: false
type: string
- name: expirationTimeBefore
in: query
description: Fetch the objects with expiration time before specified timestamp in RFC3339 format
required: false
type: string
- name: deleted
in: query
description: Specify true or false. Fetch the object with deleted marked to true/false. If not specified, object will not be filtered by "deleted" field
required: false
type: string

responses:
'200':
description: Objects response
schema:
type: array
items:
"$ref": "#/definitions/MetaData"
'404':
description: No objects found
schema:
type: string
'500':
description: Failed to retrieve the objects
schema:
type: string
package base
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
	"time"
	"github.com/open-horizon/edge-sync-service/core/security"
	"github.com/open-horizon/edge-sync-service/common"
	"github.com/open-horizon/edge-sync-service/core/communications"
	"github.com/open-horizon/edge-utilities/logger"
	"github.com/open-horizon/edge-utilities/logger/log"
	"github.com/open-horizon/edge-utilities/logger/trace"
)
const destinationsURL = "/api/v1/destinations"
const objectsURL = "/api/v1/objects/"
const organizationURL = "/api/v1/organizations/"
const getOrganizationsURL = "/api/v1/organizations"
const resendURL = "/api/v1/resend"
const securityURL = "/api/v1/security/"
const shutdownURL = "/api/v1/shutdown"
const healthURL = "/api/v1/health"
const (
	contentType     = "Content-Type"
	applicationJSON = "application/json"
)
var unauthorizedBytes = []byte("Unauthorized")
type objectUpdate struct {
	Meta common.MetaData `json:"meta"`
	Data []byte `json:"data"`
}
type webhookUpdate struct {
	Action string `json:"action"`
	URL string `json:"url"`
}
type organization struct {
	OrgID string `json:"org-id"`
	Address string `json:"address"`
}
type bulkACLUpdate struct {
	Action string `json:"action"`
	Users []common.ACLentry `json:"users"`
}
type bulkDestUpdate struct {
	Action string `json:"action"`
	Destinations []string `json:"destinations"`
}
func setupAPIServer() {
	if common.Configuration.NodeType == common.CSS {
		http.Handle(destinationsURL+"/", http.StripPrefix(destinationsURL+"/", http.HandlerFunc(handleDestinations)))
		http.Handle(securityURL, http.StripPrefix(securityURL, http.HandlerFunc(handleSecurity)))
	} else {
		http.HandleFunc(destinationsURL, handleDestinations)
	}
	http.Handle(objectsURL, http.StripPrefix(objectsURL, http.HandlerFunc(handleObjects)))
	http.HandleFunc(shutdownURL, handleShutdown)
	http.HandleFunc(resendURL, handleResend)
	http.Handle(getOrganizationsURL, http.StripPrefix(getOrganizationsURL, http.HandlerFunc(handleGetOrganizations)))
	http.Handle(organizationURL, http.StripPrefix(organizationURL, http.HandlerFunc(handleOrganizations)))
	http.HandleFunc(healthURL, handleHealth)
}
func handleDestinations(writer http.ResponseWriter, request *http.Request) {
	setResponseHeaders(writer)
	if !common.Running {
		writer.WriteHeader(http.StatusServiceUnavailable)
		return
	}
	code, userOrg, _ := security.Authenticate(request)
	if code == security.AuthFailed || code == security.AuthEdgeNode {
		writer.WriteHeader(http.StatusForbidden)
		writer.Write(unauthorizedBytes)
		return
	}
	if request.Method != http.MethodGet {
		writer.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	var orgID string
	var parts []string
	if len(request.URL.Path) != 0 {
		parts = strings.Split(request.URL.Path, "/")
		if common.Configuration.NodeType == common.CSS {
			if len(parts) == 0 {
				writer.WriteHeader(http.StatusBadRequest)
				return
			}
			orgID = parts[0]
			parts = parts[1:]
		} else {
			orgID = common.Configuration.OrgID
		}
	} else {
		if common.Configuration.NodeType == common.CSS {
			writer.WriteHeader(http.StatusBadRequest)
			return
		}
		orgID = common.Configuration.OrgID
	}
	if pathParamValid := validatePathParam(writer, orgID, "", "", "", ""); !pathParamValid {
		return
	}
	if userOrg != orgID && code != security.AuthSyncAdmin {
		writer.WriteHeader(http.StatusForbidden)
		writer.Write(unauthorizedBytes)
		return
	}
	if len(parts) == 0 || (len(parts) == 1 && len(parts[0]) == 0) {
		if dests, err := ListDestinations(orgID); err != nil {
			communications.SendErrorResponse(writer, err, "Failed to fetch the list of destinations. Error: ", 0)
		} else {
			if len(dests) == 0 {
				writer.WriteHeader(http.StatusNotFound)
			} else {
				if data, err := json.MarshalIndent(dests, "", "  "); err != nil {
					communications.SendErrorResponse(writer, err, "Failed to marshal the list of destinations. Error: ", 0)
				} else {
					writer.Header().Add(contentType, applicationJSON)
					writer.WriteHeader(http.StatusOK)
					if _, err := writer.Write(data); err != nil && log.IsLogging(logger.ERROR) {
						log.Error("Failed to write response body, error: " + err.Error())
					}
				}
			}
		}
	} else if len(parts) == 3 || (len(parts) == 4 && len(parts[3]) == 0) && parts[2] == "objects" {
		if pathParamValid := validatePathParam(writer, orgID, "", "", parts[0], parts[1]); !pathParamValid {
			return
		}
		if objects, err := GetObjectsForDestination(orgID, parts[0], parts[1]); err != nil {
			communications.SendErrorResponse(writer, err, "Failed to fetch the objects. Error: ", 0)
		} else {
			if len(objects) == 0 {
				writer.WriteHeader(http.StatusNotFound)
			} else {
				if data, err := json.MarshalIndent(objects, "", "  "); err != nil {
					communications.SendErrorResponse(writer, err, "Failed to marshal the list of objects. Error: ", 0)
				} else {
					writer.Header().Add(contentType, applicationJSON)
					writer.WriteHeader(http.StatusOK)
					if _, err := writer.Write(data); err != nil && log.IsLogging(logger.ERROR) {
						log.Error("Failed to write response body, error: " + err.Error())
					}
				}
			}
		}
	} else {
		writer.WriteHeader(http.StatusBadRequest)
	}
}
func handleResend(writer http.ResponseWriter, request *http.Request) {
	setResponseHeaders(writer)
	if !common.Running {
		writer.WriteHeader(http.StatusServiceUnavailable)
		return
	}
	code, _, _ := security.Authenticate(request)
	if code != security.AuthAdmin && code != security.AuthUser && code != security.AuthSyncAdmin {
		writer.WriteHeader(http.StatusForbidden)
		writer.Write(unauthorizedBytes)
		return
	}
	if request.Method == http.MethodPost {
		if trace.IsLogging(logger.DEBUG) {
			trace.Debug("In handleResend\n")
		}
		if err := ResendObjects(); err != nil {
			communications.SendErrorResponse(writer, err, "Failed to send resend objects request. Error: ", 0)
		} else {
			writer.WriteHeader(http.StatusNoContent)
		}
	} else {
		writer.WriteHeader(http.StatusMethodNotAllowed)
	}
}
func handleShutdown(writer http.ResponseWriter, request *http.Request) {
	setResponseHeaders(writer)
	code, _, _ := security.Authenticate(request)
	if code != security.AuthSyncAdmin {
		writer.WriteHeader(http.StatusForbidden)
		writer.Write(unauthorizedBytes)
		return
	}
	if request.Method == http.MethodPost {
		writer.WriteHeader(http.StatusNoContent)
		restart := strings.ToLower(request.URL.Query().Get("restart"))
		quiesceString := request.URL.Query().Get("quiesce")
		essUnregister := false
		if common.Configuration.NodeType == common.ESS {
			unregisterString := request.URL.Query().Get("essunregister")
			var err error
			if unregisterString != "" {
				essUnregister, err = strconv.ParseBool(unregisterString)
				if err != nil {
					writer.WriteHeader(http.StatusBadRequest)
					return
				}
			}
		}
		go func() {
			timer := time.NewTimer(time.Duration(1) * time.Second)
			<-timer.C
			quieceTime := 3
			if len(quiesceString) != 0 {
				var quieceTemp int
				_, err := fmt.Sscanf(quiesceString, "%d", &quieceTemp)
				if err == nil {
					quieceTime = quieceTemp
				}
			}
			if restart == "true" || restart == "yes" {
				blocking := waitingOnBlockChannel
				waitingOnBlockChannel = false
				Stop(quieceTime, essUnregister)
				if log.IsLogging(logger.INFO) {
					log.Info("Restarting the Sync Service")
				}
				Start("", false)
				waitingOnBlockChannel = blocking
			} else {
				Stop(quieceTime, essUnregister)
			}
		}()
	} else {
		writer.WriteHeader(http.StatusMethodNotAllowed)
	}
}
func handleObjects(writer http.ResponseWriter, request *http.Request) {
	setResponseHeaders(writer)
	if !common.Running {
		writer.WriteHeader(http.StatusServiceUnavailable)
		return
	}
	if len(request.URL.Path) != 0 {
		parts := strings.Split(request.URL.Path, "/")
		var orgID string
		if common.Configuration.NodeType == common.CSS {
			orgID = parts[0]
			parts = parts[1:]
		} else {
			orgID = common.Configuration.OrgID
		}
		if len(parts) == 0 {
			if request.Method != http.MethodGet {
				writer.WriteHeader(http.StatusMethodNotAllowed)
				return
			}
			destPolicyString := request.URL.Query().Get("destination_policy")
			destPolicy := false
			objectFilterString := request.URL.Query().Get("filters")
			objectFilter := false
			var err error
			if destPolicyString != "" {
				destPolicy, err = strconv.ParseBool(destPolicyString)
				if err == nil && destPolicy {
					handleListObjectsWithDestinationPolicy(orgID, writer, request)
					return
				}
			} else if objectFilterString != "" {
				objectFilter, err = strconv.ParseBool(objectFilterString)
				if err == nil && objectFilter {
					handleListObjectsWithFilters(orgID, writer, request)
					return
				}
			}
			writer.WriteHeader(http.StatusBadRequest)
			return
		} else if len(parts) == 1 || (len(parts) == 2 && len(parts[1]) == 0) {
			switch request.Method {
			case http.MethodGet:
				allObjectsString := request.URL.Query().Get("all_objects")
				allObjects := false
				if allObjectsString != "" {
					var err error
					allObjects, err = strconv.ParseBool(allObjectsString)
					if err != nil {
						writer.WriteHeader(http.StatusBadRequest)
						return
					}
				}
				if allObjects {
					handleListAllObjects(orgID, parts[0], writer, request)
				} else {
					receivedString := request.URL.Query().Get("received")
					received := false
					if receivedString != "" {
						var err error
						received, err = strconv.ParseBool(receivedString)
						if err != nil {
							writer.WriteHeader(http.StatusBadRequest)
							return
						}
					}
					handleListUpdatedObjects(orgID, parts[0], received, writer, request)
				}
			case http.MethodPut:
				handleWebhook(orgID, parts[0], writer, request)
			default:
				writer.WriteHeader(http.StatusMethodNotAllowed)
			}
		} else if len(parts) == 2 || (len(parts) == 3 && len(parts[2]) == 0) {
			handleObjectRequest(orgID, parts[0], parts[1], writer, request)
		} else if len(parts) == 3 || (len(parts) == 4 && len(parts[3]) == 0) {
			operation := strings.ToLower(parts[2])
			handleObjectOperation(operation, orgID, parts[0], parts[1], writer, request)
		} else {
			writer.WriteHeader(http.StatusBadRequest)
		}
	} else {
		writer.WriteHeader(http.StatusBadRequest)
	}
}
func handleObjectRequest(orgID string, objectType string, objectID string, writer http.ResponseWriter, request *http.Request) {
	if pathParamValid := validatePathParam(writer, orgID, objectType, objectID, "", ""); !pathParamValid {
		return
	}
	setResponseHeaders(writer)
	switch request.Method {
	case http.MethodGet:
		if trace.IsLogging(logger.DEBUG) {
			trace.Debug("In handleObjects. Get %s %s\n", objectType, objectID)
		}
		canAccessAllObjects, code, userID := canUserAccessObject(request, orgID, objectType, objectID, false)
		if code == security.AuthFailed {
			writer.WriteHeader(http.StatusForbidden)
			writer.Write(unauthorizedBytes)
			return
		}
		if metaData, err := GetObject(orgID, objectType, objectID); err != nil {
			communications.SendErrorResponse(writer, err, "", 0)
		} else {
			if metaData == nil {
				if canAccessAllObjects {
					writer.WriteHeader(http.StatusNotFound)
				} else {
					writer.WriteHeader(http.StatusForbidden)
					writer.Write(unauthorizedBytes)
				}
			} else {
				if metaData.Public || canAccessAllObjects {
					if trace.IsLogging(logger.DEBUG) {
						trace.Debug("In handleObjects. Object is public %t, given user can access all object for object type(%s) %t\n", metaData.Public, objectType, canAccessAllObjects)
					}
					if data, err := json.MarshalIndent(metaData, "", "  "); err != nil {
						communications.SendErrorResponse(writer, err, "Failed to marshal metadata. Error: ", 0)
					} else {
						writer.Header().Add(contentType, applicationJSON)
						writer.WriteHeader(http.StatusOK)
						if _, err := writer.Write(data); err != nil && log.IsLogging(logger.ERROR) {
							log.Error("Failed to write response body, error: " + err.Error())
						}
					}
				} else {
					if trace.IsLogging(logger.DEBUG) {
						trace.Debug("In handleObjects. Given user %s doesn't have access to object %s %s in org %s\n", userID, objectType, objectID, orgID)
					}
					writer.WriteHeader(http.StatusForbidden)
					writer.Write(unauthorizedBytes)
				}
			}
		}
	case http.MethodDelete:
		if trace.IsLogging(logger.DEBUG) {
			trace.Debug("In handleObjects. Delete %s %s\n", objectType, objectID)
		}
		if _, code, _ := canUserAccessObject(request, orgID, objectType, objectID, false); code == security.AuthFailed {
			writer.WriteHeader(http.StatusForbidden)
			writer.Write(unauthorizedBytes)
			return
		} else if (code == security.AuthUser || code == security.AuthNodeUser) && common.Configuration.NodeType == common.CSS {
			if metaData, err := GetObject(orgID, objectType, objectID); err != nil {
				communications.SendErrorResponse(writer, err, "", 0)
				return
			} else {
				if metaData == nil {
					writer.WriteHeader(http.StatusNotFound)
					return
				}
				validateUser, userOrgID, userID := security.CanUserCreateObject(request, orgID, metaData)
				if trace.IsLogging(logger.DEBUG) {
					trace.Debug("In handleObjects. Delete %s %s. ValidateUser %t %s %s\n", objectType, objectID, validateUser, userOrgID, userID)
				}
				if !validateUser {
					writer.WriteHeader(http.StatusForbidden)
					writer.Write(unauthorizedBytes)
					return
				}
			}
		}
		if err := DeleteObject(orgID, objectType, objectID); err != nil {
			communications.SendErrorResponse(writer, err, "Failed to delete the object. Error: ", 0)
		} else {
			writer.WriteHeader(http.StatusNoContent)
		}
	case http.MethodPut:
		handleUpdateObject(orgID, objectType, objectID, writer, request)
	default:
		writer.WriteHeader(http.StatusMethodNotAllowed)
	}
}