- Add queryTimed(), logPerf(), flushPerfBuffer() to environment.cfm - Auto-create ApiPerfLogs table on first flush - Hook logPerf into Application.cfm apiAbort for automatic tracking - Initialize request perf counters in Application.cfm - Remove local apiAbort() overrides from 7 endpoints - Instrument 12 high-traffic endpoints with logPerf calls - Buffer metrics in application scope, batch INSERT every 100 requests - 30-day auto-cleanup with probabilistic trigger Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
165 lines
5.1 KiB
Text
165 lines
5.1 KiB
Text
<cfsetting showdebugoutput="false">
|
|
<cfsetting enablecfoutputonly="true">
|
|
|
|
<cfcontent type="application/json; charset=utf-8" reset="true">
|
|
<cfheader name="Cache-Control" value="no-store">
|
|
|
|
<cfscript>
|
|
function apiAbort(obj) {
|
|
writeOutput(serializeJSON(obj));
|
|
abort;
|
|
}
|
|
|
|
function readJsonBody() {
|
|
raw = toString(getHttpRequestData().content);
|
|
if (isNull(raw) || len(trim(raw)) EQ 0) return {};
|
|
try {
|
|
parsed = deserializeJSON(raw);
|
|
} catch(any e) {
|
|
apiAbort({ OK=false, ERROR="bad_json", MESSAGE="Invalid JSON body" });
|
|
}
|
|
if (!isStruct(parsed)) return {};
|
|
return parsed;
|
|
}
|
|
|
|
data = readJsonBody();
|
|
|
|
if (!structKeyExists(data, "BeaconID") || !isNumeric(data.BeaconID) || int(data.BeaconID) LTE 0) {
|
|
apiAbort({ OK=false, ERROR="missing_beacon_id", MESSAGE="BeaconID is required" });
|
|
}
|
|
|
|
beaconId = int(data.BeaconID);
|
|
</cfscript>
|
|
|
|
<!--- Get beacon info first --->
|
|
<cfquery name="qBeacon" datasource="payfrit">
|
|
SELECT ID, Name, UUID, BusinessID
|
|
FROM Beacons
|
|
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
|
|
AND IsActive = 1
|
|
LIMIT 1
|
|
</cfquery>
|
|
|
|
<cfif qBeacon.recordCount EQ 0>
|
|
<cfoutput>#serializeJSON({ OK=false, ERROR="not_found", MESSAGE="Beacon not found or inactive" })#</cfoutput>
|
|
<cfabort>
|
|
</cfif>
|
|
|
|
<!--- Get all businesses that have assignments to this beacon (via service points or join table) --->
|
|
<cfquery name="qAssignments" datasource="payfrit">
|
|
SELECT
|
|
sp.BusinessID,
|
|
sp.ID AS ServicePointID,
|
|
biz.Name AS BusinessName,
|
|
biz.ParentBusinessID,
|
|
sp.Name AS ServicePointName
|
|
FROM ServicePoints sp
|
|
INNER JOIN Businesses biz ON biz.ID = sp.BusinessID
|
|
WHERE sp.BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
|
|
AND sp.IsActive = 1
|
|
|
|
UNION
|
|
|
|
SELECT
|
|
lt.BusinessID,
|
|
0 AS ServicePointID,
|
|
biz.Name AS BusinessName,
|
|
biz.ParentBusinessID,
|
|
'' AS ServicePointName
|
|
FROM lt_BeaconsID_BusinessesID lt
|
|
INNER JOIN Businesses biz ON biz.ID = lt.BusinessID
|
|
WHERE lt.BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
|
|
AND lt.BusinessID NOT IN (
|
|
SELECT sp2.BusinessID FROM ServicePoints sp2
|
|
WHERE sp2.BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#"> AND sp2.IsActive = 1
|
|
)
|
|
|
|
ORDER BY ParentBusinessID IS NULL DESC, BusinessName ASC
|
|
</cfquery>
|
|
|
|
<!--- Check if any assigned business is a parent (has children) --->
|
|
<cfset parentBusinessID = 0>
|
|
<cfloop query="qAssignments">
|
|
<!--- Check if this business has children --->
|
|
<cfquery name="qChildren" datasource="payfrit">
|
|
SELECT COUNT(*) as cnt FROM Businesses
|
|
WHERE ParentBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#qAssignments.BusinessID#">
|
|
</cfquery>
|
|
<cfif qChildren.cnt GT 0>
|
|
<cfset parentBusinessID = qAssignments.BusinessID>
|
|
<cfbreak>
|
|
</cfif>
|
|
</cfloop>
|
|
|
|
<!--- Build response with array of businesses --->
|
|
<cfset businesses = []>
|
|
|
|
<!--- If beacon is assigned to a parent, return the child businesses instead --->
|
|
<cfif parentBusinessID GT 0>
|
|
<!--- Get parent business info for header image --->
|
|
<cfquery name="qParent" datasource="payfrit">
|
|
SELECT Name, HeaderImageExtension
|
|
FROM Businesses
|
|
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#parentBusinessID#">
|
|
</cfquery>
|
|
<cfquery name="qChildBusinesses" datasource="payfrit">
|
|
SELECT
|
|
ID,
|
|
Name,
|
|
ParentBusinessID,
|
|
HeaderImageExtension
|
|
FROM Businesses
|
|
WHERE ParentBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#parentBusinessID#">
|
|
ORDER BY Name ASC
|
|
</cfquery>
|
|
<cfloop query="qChildBusinesses">
|
|
<cfset arrayAppend(businesses, {
|
|
"BusinessID" = qChildBusinesses.ID,
|
|
"Name" = qChildBusinesses.Name,
|
|
"ServicePointID" = qAssignments.ServicePointID,
|
|
"ServicePointName" = qAssignments.ServicePointName,
|
|
"IsParent" = false,
|
|
"ParentBusinessID" = parentBusinessID
|
|
})>
|
|
</cfloop>
|
|
<cfelse>
|
|
<!--- Normal case: return directly assigned businesses --->
|
|
<cfloop query="qAssignments">
|
|
<cfset arrayAppend(businesses, {
|
|
"BusinessID" = qAssignments.BusinessID,
|
|
"Name" = qAssignments.BusinessName,
|
|
"ServicePointID" = qAssignments.ServicePointID,
|
|
"ServicePointName" = qAssignments.ServicePointName,
|
|
"IsParent" = isNull(qAssignments.ParentBusinessID) OR qAssignments.ParentBusinessID EQ 0
|
|
})>
|
|
</cfloop>
|
|
</cfif>
|
|
|
|
<cfset response = {
|
|
"OK" = true,
|
|
"ERROR" = "",
|
|
"BEACON" = {
|
|
"BeaconID" = qBeacon.ID,
|
|
"Name" = qBeacon.Name,
|
|
"UUID" = qBeacon.UUID
|
|
},
|
|
"BUSINESSES" = businesses,
|
|
"BUSINESS" = arrayLen(businesses) GT 0 ? businesses[1] : {},
|
|
"SERVICEPOINT" = arrayLen(businesses) GT 0 ? {
|
|
"ServicePointID" = businesses[1].ServicePointID,
|
|
"Name" = businesses[1].ServicePointName,
|
|
"IsActive" = true
|
|
} : {}
|
|
}>
|
|
|
|
<!--- Add parent info if this is a parent-child scenario --->
|
|
<cfif parentBusinessID GT 0>
|
|
<cfset response["PARENT"] = {
|
|
"BusinessID" = parentBusinessID,
|
|
"Name" = qParent.Name,
|
|
"HeaderImageExtension" = len(trim(qParent.HeaderImageExtension)) ? qParent.HeaderImageExtension : ""
|
|
}>
|
|
</cfif>
|
|
|
|
<cfscript>try{logPerf(0);}catch(any e){}</cfscript>
|
|
<cfoutput>#serializeJSON(response)#</cfoutput>
|