mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-10 04:36:14 +00:00
refactor(service): stub the unused /accounts/* mirror with a 501 "report it" handler (refs #451)
Shrink the route surface the #451 refactor must preserve by retiring the /accounts/{account}/* compatibility mirror. Across the full recording corpus (all _/backup/*, _/mitm, _/i195, _/issue-94, captures + data/ + tests/, 139k+ .http files) no speaker or app uses the /accounts prefix, and every operation it offered is served by the /streaming/account/* paths real clients actually use. - New HandleUnsupported: returns 501 and logs the full request + client IP + a "please report this" message, so any real-world use surfaces instead of being silently dropped, and the prefix becomes a clean removal candidate. - Re-point every /accounts/* route to it. The frozen /streaming/* contract is left entirely on its real handlers (those stay even where our corpus didn't exercise them — absence of capture is not proof of disuse). - Migrate the integration tests off the /accounts mirror onto their recorded /streaming/account/* equivalents (register/unregister/spotify_full_flow), then pin the mirror's 501 contract in unsupported_routes.http. - Router + frozen-route-coverage golden files updated accordingly. make test-http-client: 91 requests, 0 failed. go test + golangci-lint clean. Note for release time: call out the intentional /accounts/* 501 breakage in the release notes' Noteworthy section (use /streaming/account/* instead). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
44f3ccfc18
commit
ea6ee3e097
@@ -183,6 +183,7 @@ test-http-client:
|
||||
/workdir/get_speaker_auth.http \
|
||||
/workdir/get_blacklist.http \
|
||||
/workdir/post_alexa_certificate.http \
|
||||
/workdir/unsupported_routes.http \
|
||||
/workdir/spotify_full_flow.http \
|
||||
/workdir/customer_support.http \
|
||||
/workdir/power_on.http \
|
||||
|
||||
@@ -1225,6 +1225,9 @@ func setupRouter(server *handlers.Server, stockholmHandler *stockholm.Handler) *
|
||||
r.Get("/{device}/group/member", server.HandleMargeDeviceGroupMember)
|
||||
})
|
||||
|
||||
// Speakers POST to /group/ (with trailing slash) when forwarding
|
||||
// the addGroup payload to Marge during stereo-pair formation --
|
||||
// see issue #252. Register both forms so chi accepts either.
|
||||
// Speakers POST to /group/ (with trailing slash) when forwarding
|
||||
// the addGroup payload to Marge during stereo-pair formation --
|
||||
// see issue #252. Register both forms so chi accepts either.
|
||||
@@ -1266,31 +1269,38 @@ func setupRouter(server *handlers.Server, stockholmHandler *stockholm.Handler) *
|
||||
r.Get("/resources/api_versions.xml", server.HandleMargeAPIVersions)
|
||||
})
|
||||
|
||||
// The /accounts/* group mirrored /streaming/account/* for compatibility, but
|
||||
// no speaker or app was ever observed using this prefix in the recording
|
||||
// corpus (the integration tests that exercised it were migrated onto the
|
||||
// /streaming equivalents). The whole mirror is therefore treated as unused
|
||||
// and stubbed (HandleUnsupported): it logs + 501s so any real-world use
|
||||
// surfaces instead of being silently dropped, leaving the prefix a clean
|
||||
// removal candidate for the #451 refactor.
|
||||
r.Route("/accounts", func(r chi.Router) {
|
||||
r.Route("/{account}", func(r chi.Router) {
|
||||
r.Get("/full", server.HandleMargeAccountFull)
|
||||
r.Get("/sources", server.HandleMargeAccountSources)
|
||||
r.Get("/devices", server.HandleMargeAccountDevices)
|
||||
r.Get("/full", server.HandleUnsupported)
|
||||
r.Get("/sources", server.HandleUnsupported)
|
||||
r.Get("/devices", server.HandleUnsupported)
|
||||
|
||||
r.Post("/devices", server.HandleMargeAddDevice)
|
||||
r.Post("/devices", server.HandleUnsupported)
|
||||
|
||||
r.Delete("/devices/{device}", server.HandleMargeRemoveDevice)
|
||||
r.Get("/devices/{device}/group", server.HandleMargeDeviceGroup)
|
||||
r.Get("/devices/{device}/group/", server.HandleMargeDeviceGroup)
|
||||
r.Get("/devices/{device}/group/server", server.HandleMargeDeviceGroupServer)
|
||||
r.Get("/devices/{device}/group/member", server.HandleMargeDeviceGroupMember)
|
||||
r.Delete("/devices/{device}", server.HandleUnsupported)
|
||||
r.Get("/devices/{device}/group", server.HandleUnsupported)
|
||||
r.Get("/devices/{device}/group/", server.HandleUnsupported)
|
||||
r.Get("/devices/{device}/group/server", server.HandleUnsupported)
|
||||
r.Get("/devices/{device}/group/member", server.HandleUnsupported)
|
||||
|
||||
r.Post("/group", server.HandleMargeAddGroup)
|
||||
r.Post("/group/", server.HandleMargeAddGroup)
|
||||
r.Post("/group/{groupId}", server.HandleMargeModifyGroup)
|
||||
r.Delete("/group/{groupId}", server.HandleMargeDeleteGroup)
|
||||
r.Delete("/group", server.HandleMargeDeleteAccountGroups)
|
||||
r.Delete("/group/", server.HandleMargeDeleteAccountGroups)
|
||||
r.Get("/devices/{device}/presets", server.HandleMargePresets)
|
||||
r.Get("/devices/{device}/recents", server.HandleMargeRecents)
|
||||
r.Post("/group", server.HandleUnsupported)
|
||||
r.Post("/group/", server.HandleUnsupported)
|
||||
r.Post("/group/{groupId}", server.HandleUnsupported)
|
||||
r.Delete("/group/{groupId}", server.HandleUnsupported)
|
||||
r.Delete("/group", server.HandleUnsupported)
|
||||
r.Delete("/group/", server.HandleUnsupported)
|
||||
r.Get("/devices/{device}/presets", server.HandleUnsupported)
|
||||
r.Get("/devices/{device}/recents", server.HandleUnsupported)
|
||||
|
||||
r.Post("/devices/{device}/presets/{presetNumber}", server.HandleMargeUpdatePreset)
|
||||
r.Post("/devices/{device}/recents", server.HandleMargeAddRecent)
|
||||
r.Post("/devices/{device}/presets/{presetNumber}", server.HandleUnsupported)
|
||||
r.Post("/devices/{device}/recents", server.HandleUnsupported)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
DELETE /accounts/{account}/group
|
||||
DELETE /accounts/{account}/group/
|
||||
DELETE /accounts/{account}/group/{groupId}
|
||||
DELETE /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter
|
||||
DELETE /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter/*
|
||||
DELETE /streaming/account/{account}/group
|
||||
GET /accounts/{account}/devices
|
||||
GET /accounts/{account}/devices/{device}/group
|
||||
GET /accounts/{account}/devices/{device}/group/
|
||||
GET /accounts/{account}/devices/{device}/group/member
|
||||
GET /accounts/{account}/devices/{device}/group/server
|
||||
GET /accounts/{account}/devices/{device}/recents
|
||||
GET /accounts/{account}/full
|
||||
GET /bmx-icons/*
|
||||
GET /bmx/tunein/v1/navigate
|
||||
GET /bmx/tunein/v1/navigate/*
|
||||
@@ -27,10 +17,6 @@ GET /streaming/account/{account}/device/{device}/group/server
|
||||
GET /streaming/account/{account}/device/{device}/recent
|
||||
GET /streaming/account/{account}/presets
|
||||
GET /streaming/device_setting/account/{account}/device/{device}/device_settings
|
||||
POST /accounts/{account}/devices/{device}/recents
|
||||
POST /accounts/{account}/group
|
||||
POST /accounts/{account}/group/
|
||||
POST /accounts/{account}/group/{groupId}
|
||||
POST /core02/svc-bmx-adapter-orion/prod/orion/token
|
||||
POST /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter
|
||||
POST /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter/*
|
||||
|
||||
+19
-19
@@ -1,9 +1,9 @@
|
||||
CONNECT /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter handlers.(*Server).HandleSiriusXMLiveAdapter-fm
|
||||
CONNECT /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter/* handlers.(*Server).HandleSiriusXMLiveAdapterSubpath-fm
|
||||
DELETE /accounts/{account}/devices/{device} handlers.(*Server).HandleMargeRemoveDevice-fm
|
||||
DELETE /accounts/{account}/group handlers.(*Server).HandleMargeDeleteAccountGroups-fm
|
||||
DELETE /accounts/{account}/group/ handlers.(*Server).HandleMargeDeleteAccountGroups-fm
|
||||
DELETE /accounts/{account}/group/{groupId} handlers.(*Server).HandleMargeDeleteGroup-fm
|
||||
DELETE /accounts/{account}/devices/{device} handlers.(*Server).HandleUnsupported-fm
|
||||
DELETE /accounts/{account}/group handlers.(*Server).HandleUnsupported-fm
|
||||
DELETE /accounts/{account}/group/ handlers.(*Server).HandleUnsupported-fm
|
||||
DELETE /accounts/{account}/group/{groupId} handlers.(*Server).HandleUnsupported-fm
|
||||
DELETE /bmx/tunein/v1/favorite/{stationID} handlers.(*Server).HandleTuneInDeleteFavorite-fm
|
||||
DELETE /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter handlers.(*Server).HandleSiriusXMLiveAdapter-fm
|
||||
DELETE /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter/* handlers.(*Server).HandleSiriusXMLiveAdapterSubpath-fm
|
||||
@@ -18,15 +18,15 @@ DELETE /streaming/account/{account}/group handlers.(
|
||||
DELETE /streaming/account/{account}/group/ handlers.(*Server).HandleMargeDeleteAccountGroups-fm
|
||||
DELETE /streaming/account/{account}/group/{groupId} handlers.(*Server).HandleMargeDeleteGroup-fm
|
||||
GET / handlers.(*Server).HandleRoot-fm
|
||||
GET /accounts/{account}/devices handlers.(*Server).HandleMargeAccountDevices-fm
|
||||
GET /accounts/{account}/devices/{device}/group handlers.(*Server).HandleMargeDeviceGroup-fm
|
||||
GET /accounts/{account}/devices/{device}/group/ handlers.(*Server).HandleMargeDeviceGroup-fm
|
||||
GET /accounts/{account}/devices/{device}/group/member handlers.(*Server).HandleMargeDeviceGroupMember-fm
|
||||
GET /accounts/{account}/devices/{device}/group/server handlers.(*Server).HandleMargeDeviceGroupServer-fm
|
||||
GET /accounts/{account}/devices/{device}/presets handlers.(*Server).HandleMargePresets-fm
|
||||
GET /accounts/{account}/devices/{device}/recents handlers.(*Server).HandleMargeRecents-fm
|
||||
GET /accounts/{account}/full handlers.(*Server).HandleMargeAccountFull-fm
|
||||
GET /accounts/{account}/sources handlers.(*Server).HandleMargeAccountSources-fm
|
||||
GET /accounts/{account}/devices handlers.(*Server).HandleUnsupported-fm
|
||||
GET /accounts/{account}/devices/{device}/group handlers.(*Server).HandleUnsupported-fm
|
||||
GET /accounts/{account}/devices/{device}/group/ handlers.(*Server).HandleUnsupported-fm
|
||||
GET /accounts/{account}/devices/{device}/group/member handlers.(*Server).HandleUnsupported-fm
|
||||
GET /accounts/{account}/devices/{device}/group/server handlers.(*Server).HandleUnsupported-fm
|
||||
GET /accounts/{account}/devices/{device}/presets handlers.(*Server).HandleUnsupported-fm
|
||||
GET /accounts/{account}/devices/{device}/recents handlers.(*Server).HandleUnsupported-fm
|
||||
GET /accounts/{account}/full handlers.(*Server).HandleUnsupported-fm
|
||||
GET /accounts/{account}/sources handlers.(*Server).HandleUnsupported-fm
|
||||
GET /bmx-icons/* handlers.(*Server).HandleBmxIcons
|
||||
GET /bmx/registry/v1/services handlers.(*Server).HandleBMXRegistry-fm
|
||||
GET /bmx/registry/v1/servicesAvailability handlers.(*Server).HandleBMXServicesAvailability-fm
|
||||
@@ -109,12 +109,12 @@ OPTIONS /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter handler
|
||||
OPTIONS /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter/* handlers.(*Server).HandleSiriusXMLiveAdapterSubpath-fm
|
||||
PATCH /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter handlers.(*Server).HandleSiriusXMLiveAdapter-fm
|
||||
PATCH /core02/svc-bmx-adapter-siriusxm-everest-eco1/prod/live-adapter/* handlers.(*Server).HandleSiriusXMLiveAdapterSubpath-fm
|
||||
POST /accounts/{account}/devices handlers.(*Server).HandleMargeAddDevice-fm
|
||||
POST /accounts/{account}/devices/{device}/presets/{presetNumber} handlers.(*Server).HandleMargeUpdatePreset-fm
|
||||
POST /accounts/{account}/devices/{device}/recents handlers.(*Server).HandleMargeAddRecent-fm
|
||||
POST /accounts/{account}/group handlers.(*Server).HandleMargeAddGroup-fm
|
||||
POST /accounts/{account}/group/ handlers.(*Server).HandleMargeAddGroup-fm
|
||||
POST /accounts/{account}/group/{groupId} handlers.(*Server).HandleMargeModifyGroup-fm
|
||||
POST /accounts/{account}/devices handlers.(*Server).HandleUnsupported-fm
|
||||
POST /accounts/{account}/devices/{device}/presets/{presetNumber} handlers.(*Server).HandleUnsupported-fm
|
||||
POST /accounts/{account}/devices/{device}/recents handlers.(*Server).HandleUnsupported-fm
|
||||
POST /accounts/{account}/group handlers.(*Server).HandleUnsupported-fm
|
||||
POST /accounts/{account}/group/ handlers.(*Server).HandleUnsupported-fm
|
||||
POST /accounts/{account}/group/{groupId} handlers.(*Server).HandleUnsupported-fm
|
||||
POST /alexa/certificate handlers.(*Server).HandleAlexaCertificate-fm
|
||||
POST /bmx/tunein/v1/favorite/{stationID} handlers.(*Server).HandleTuneInFavorite-fm
|
||||
POST /bmx/tunein/v1/report handlers.(*Server).HandleTuneInReport-fm
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// unsupportedReportURL is where we ask operators to report a real client that
|
||||
// depends on an endpoint we treat as unused.
|
||||
const unsupportedReportURL = "https://github.com/gesellix/Bose-SoundTouch/issues"
|
||||
|
||||
// maxUnsupportedBodyLog caps how much of an unexpected request body we log.
|
||||
const maxUnsupportedBodyLog = 2048
|
||||
|
||||
// HandleUnsupported is wired to frozen routes that no real speaker or app was
|
||||
// ever observed using (verified against the recorded interaction corpus). It
|
||||
// exists so the API surface the refactor (issue #451) has to preserve stays as
|
||||
// small as possible: these routes are candidates for removal, but rather than
|
||||
// drop them blind we make them fail loudly and observably.
|
||||
//
|
||||
// It returns 501 Not Implemented and logs the full request (method, path,
|
||||
// query, client IP, user-agent and a capped, sanitized body) plus a message
|
||||
// asking the operator to report it, so that if some device or old app actually
|
||||
// relies on the route we find out and restore it instead of silently breaking
|
||||
// it during the refactor.
|
||||
func (s *Server) HandleUnsupported(w http.ResponseWriter, r *http.Request) {
|
||||
client := clientHostFromRemoteAddr(r.RemoteAddr)
|
||||
|
||||
var body []byte
|
||||
if r.Body != nil {
|
||||
body, _ = io.ReadAll(io.LimitReader(r.Body, maxUnsupportedBodyLog))
|
||||
}
|
||||
|
||||
log.Printf("[unsupported] %s %s%s called by client=%s ua=%q — this endpoint is treated as unused and returns 501. "+
|
||||
"If a speaker or app you rely on needs it, please report it at %s so we can keep it. body=%q",
|
||||
sanitizeLog(r.Method),
|
||||
sanitizeLog(r.URL.Path),
|
||||
sanitizeLog(querySuffix(r)),
|
||||
sanitizeLog(client),
|
||||
sanitizeLog(r.UserAgent()),
|
||||
unsupportedReportURL,
|
||||
sanitizeLog(string(body)),
|
||||
)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
_, _ = w.Write([]byte(`{"error":"not_implemented","message":"This endpoint is not implemented by AfterTouch. ` +
|
||||
`If your speaker or app depends on it, please report it at ` + unsupportedReportURL + `"}`))
|
||||
}
|
||||
|
||||
// querySuffix returns "?<rawquery>" or "" so the log line shows the query
|
||||
// without an empty trailing "?".
|
||||
func querySuffix(r *http.Request) string {
|
||||
if r.URL.RawQuery == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return "?" + r.URL.RawQuery
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
### POST /{{accountId}}/devices (Register Device)
|
||||
POST {{host}}/accounts/{{accountId}}/devices
|
||||
### POST /streaming/account/{{accountId}}/device/ (Register Device)
|
||||
POST {{host}}/streaming/account/{{accountId}}/device/
|
||||
Content-Type: application/vnd.bose.streaming-v1.2+xml
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Authorization: Basic admin change_me!
|
||||
|
||||
# 4. Verify Marge Source Registration and extract Spotify Source ID
|
||||
# @name Get Marge Sources
|
||||
GET {{host}}/accounts/{{accountId}}/sources
|
||||
GET {{host}}/streaming/account/{{accountId}}/sources
|
||||
|
||||
> {%
|
||||
client.test("Spotify source registered in Marge", function() {
|
||||
@@ -94,7 +94,7 @@ GET {{host}}/accounts/{{accountId}}/sources
|
||||
|
||||
# 5. Add a Spotify Preset
|
||||
# @name Update Preset
|
||||
POST {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/presets/1
|
||||
PUT {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}/preset/1
|
||||
Content-Type: application/xml
|
||||
|
||||
<preset id="1">
|
||||
@@ -114,7 +114,7 @@ Content-Type: application/xml
|
||||
|
||||
# 6. Verify Preset in Marge
|
||||
# @name Get Presets
|
||||
GET {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/presets
|
||||
GET {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}/presets
|
||||
|
||||
> {%
|
||||
client.test("Verify preset content", function() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
### DELETE /{{accountId}}/devices/{{deviceId}} (Unregister Device)
|
||||
DELETE {{host}}/accounts/{{accountId}}/devices/{{deviceId}}
|
||||
### DELETE /streaming/account/{{accountId}}/device/{{deviceId}} (Unregister Device)
|
||||
DELETE {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
> {%
|
||||
@@ -7,13 +7,3 @@ Authorization: Bearer {{token}}
|
||||
client.assert(response.status === 200, "Response status is not 200");
|
||||
});
|
||||
%}
|
||||
|
||||
### DELETE /streaming/account/{{accountId}}/device/{{deviceId}} (Unregister Device Variant)
|
||||
DELETE {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
> {%
|
||||
client.test("Device unregistered successfully (variant)", function() {
|
||||
client.assert(response.status === 200, "Response status is not 200");
|
||||
});
|
||||
%}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
### Unsupported (zero-usage) /accounts/* mirror -> 501
|
||||
###
|
||||
### The /accounts/* group mirrored /streaming/account/* for compatibility, but no
|
||||
### speaker or app was ever observed using this prefix across the full recording
|
||||
### corpus, and every operation it offers is provably served via the
|
||||
### /streaming/account/* paths that real clients DO use (the integration tests
|
||||
### were migrated onto those). The whole mirror is therefore wired to
|
||||
### HandleUnsupported, which logs the full request + client IP + a "please report
|
||||
### this" message and returns 501, so any real-world use surfaces instead of
|
||||
### being silently broken and the prefix becomes a clean removal candidate for
|
||||
### the #451 refactor. These tests pin that 501 contract.
|
||||
###
|
||||
### NOTE: the real /streaming/account/* (and /streaming/...) routes are NOT
|
||||
### stubbed — those are the frozen Bose contract and stay on their real handlers
|
||||
### even where our corpus didn't exercise them.
|
||||
|
||||
### GET /accounts/{a}/full
|
||||
GET {{host}}/accounts/{{accountId}}/full
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### GET /accounts/{a}/sources (real clients use /streaming/account/{a}/sources)
|
||||
GET {{host}}/accounts/{{accountId}}/sources
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### GET /accounts/{a}/devices
|
||||
GET {{host}}/accounts/{{accountId}}/devices
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### POST /accounts/{a}/devices (real clients use POST /streaming/account/{a}/device/)
|
||||
POST {{host}}/accounts/{{accountId}}/devices
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Content-Type: application/xml
|
||||
|
||||
<device/>
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### DELETE /accounts/{a}/devices/{deviceId} (real clients use DELETE /streaming/account/{a}/device/{d})
|
||||
DELETE {{host}}/accounts/{{accountId}}/devices/{{deviceId}}
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### GET /accounts/{a}/devices/{deviceId}/group
|
||||
GET {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/group
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### GET /accounts/{a}/devices/{deviceId}/group/
|
||||
GET {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/group/
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### GET /accounts/{a}/devices/{deviceId}/group/server
|
||||
GET {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/group/server
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### GET /accounts/{a}/devices/{deviceId}/group/member
|
||||
GET {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/group/member
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### GET /accounts/{a}/devices/{deviceId}/presets (real clients use /streaming/account/{a}/device/{d}/presets)
|
||||
GET {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/presets
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### GET /accounts/{a}/devices/{deviceId}/recents
|
||||
GET {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/recents
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### POST /accounts/{a}/devices/{deviceId}/recents
|
||||
POST {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/recents
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Content-Type: application/xml
|
||||
|
||||
<recent/>
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### POST /accounts/{a}/devices/{deviceId}/presets/{n} (real clients use PUT /streaming/account/{a}/device/{d}/preset/{n})
|
||||
POST {{host}}/accounts/{{accountId}}/devices/{{deviceId}}/presets/1
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Content-Type: application/xml
|
||||
|
||||
<preset/>
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### POST /accounts/{a}/group
|
||||
POST {{host}}/accounts/{{accountId}}/group
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Content-Type: application/xml
|
||||
|
||||
<group/>
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### POST /accounts/{a}/group/
|
||||
POST {{host}}/accounts/{{accountId}}/group/
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Content-Type: application/xml
|
||||
|
||||
<group/>
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### POST /accounts/{a}/group/{groupId}
|
||||
POST {{host}}/accounts/{{accountId}}/group/1
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
Content-Type: application/xml
|
||||
|
||||
<group/>
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### DELETE /accounts/{a}/group
|
||||
DELETE {{host}}/accounts/{{accountId}}/group
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### DELETE /accounts/{a}/group/
|
||||
DELETE {{host}}/accounts/{{accountId}}/group/
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
|
||||
### DELETE /accounts/{a}/group/{groupId}
|
||||
DELETE {{host}}/accounts/{{accountId}}/group/1
|
||||
User-Agent: Bose_Lisa/27.0.6
|
||||
|
||||
> {% client.test("501", function () { client.assert(response.status === 501, "got " + response.status); }); %}
|
||||
Reference in New Issue
Block a user