mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-28 18:22:05 +00:00
Multi select upstream providers to send spots to #95
This commit is contained in:
+23
-21
@@ -23,6 +23,7 @@ info:
|
||||
* **Breaking change:** POST `/spot` now expects `activities` (a list) and `activity_refs` in the `spot` object, rather than `sig` and `sig_refs`.
|
||||
* **Breaking change:** In the `/options` response, `sigs` has been renamed to `activities`, and within each activity, `sig_type` has been renamed to `activity_type`.
|
||||
* **Breaking change:** In the `/status` response, `sig_ref_data_providers` has been renamed to `activity_ref_data_providers`, and within each provider, `sig_name` has been renamed to `activity_name`.
|
||||
* **Breaking change:** In the POST `/spot` `handling` object, `submit_upstream` and `upstream_provider` have been replaced by `upstream_providers`, a list of provider names, so a spot can be sent to multiple upstream providers at once. `upstream_credentials` is now a map of provider name to that provider's credentials.
|
||||
|
||||
#### Upgrading a client from v2 to v3 API endpoints
|
||||
|
||||
@@ -940,6 +941,7 @@ components:
|
||||
- BIWOTA
|
||||
- COTA
|
||||
- PGA
|
||||
- Railways
|
||||
- Toilets
|
||||
example: POTA
|
||||
|
||||
@@ -1381,30 +1383,30 @@ components:
|
||||
description: >
|
||||
Optional server-side instructions for how to process this spot submission.
|
||||
properties:
|
||||
submit_upstream:
|
||||
type: boolean
|
||||
upstream_providers:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: >
|
||||
If true, forward the spot to an external upstream provider (e.g. POTA, SOTA) rather than only adding it
|
||||
to this Spothole server. Requires `upstream_provider` to be set. Check `spot_submit_providers` in the
|
||||
`/options` response to see which activities and providers support this.
|
||||
default: false
|
||||
upstream_provider:
|
||||
type: string
|
||||
description: >
|
||||
Name of the upstream provider to submit the spot to, e.g. "POTA" or "SOTA". Must
|
||||
match one of the provider names returned in `spot_submit_providers` for the chosen activity.
|
||||
example: POTA
|
||||
Names of upstream providers to forward the spot to (e.g. "POTA", "SOTA"), in addition to or instead
|
||||
of only adding it to this Spothole server. Each name must match one of the provider names returned in
|
||||
`spot_submit_providers` in the `/options` response for the chosen activity. Omit or leave empty to
|
||||
add the spot to this Spothole server only.
|
||||
example: [ POTA, ParksNPeaks ]
|
||||
upstream_credentials:
|
||||
type: object
|
||||
description: >
|
||||
Provider-specific credentials required to authenticate the upstream submission.
|
||||
The required keys depend on the provider. Credentials are used only for the upstream
|
||||
call and are never stored by Spothole.
|
||||
A map of provider name to the provider-specific credentials required to authenticate the upstream
|
||||
submission to that provider. The required keys depend on the provider. Credentials are used only for
|
||||
the upstream call and are never stored by Spothole.
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
example:
|
||||
user_id: "12345"
|
||||
api_key: "abc123"
|
||||
ParksNPeaks:
|
||||
user_id: "12345"
|
||||
api_key: "abc123"
|
||||
captcha_token:
|
||||
type: string
|
||||
description: >
|
||||
@@ -2294,9 +2296,9 @@ components:
|
||||
type: object
|
||||
description: >
|
||||
A map of activity name to a list of provider names that support upstream spot submission for that
|
||||
activity. If an activity appears as a key here, the POST /spot endpoint accepts `submit_upstream: true`
|
||||
for spots with that activity, and will forward the spot to one of the listed providers. Omitted if no
|
||||
providers support upstream submission.
|
||||
activity. If an activity appears as a key here, the POST /spot endpoint accepts these provider names in
|
||||
`upstream_providers` for spots with that activity, and will forward the spot to any of the listed
|
||||
providers. Omitted if no providers support upstream submission.
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
|
||||
+50
-42
@@ -63,46 +63,46 @@ function updateUpstreamArea() {
|
||||
|
||||
$("#upstream-area").show();
|
||||
|
||||
// Update the provider selector
|
||||
$("#upstream-provider-select").empty();
|
||||
// Rebuild the provider checkboxes, keeping any that were already checked for the previous activity
|
||||
const previouslyChecked = getSelectedUpstreamProviders();
|
||||
const container = $("#upstream-providers").empty();
|
||||
$.each(providers, function (i, name) {
|
||||
$("#upstream-provider-select").append($('<option>', {value: name, text: name}));
|
||||
const id = "submit-upstream-" + i;
|
||||
const row = $("<div class='row g-2 align-items-center mb-1'></div>");
|
||||
const check = $("<div class='form-check'></div>");
|
||||
check.append($("<input class='form-check-input upstream-provider-check' type='checkbox'>")
|
||||
.attr("id", id).val(name).prop("checked", previouslyChecked.includes(name)));
|
||||
check.append($("<label class='form-check-label'></label>").attr("for", id).text(name));
|
||||
row.append($("<div class='col-auto'></div>").append(check));
|
||||
|
||||
// Add a credentials button if this provider has an authentication mechanism and we need input from the user
|
||||
if (PROVIDER_CREDENTIAL_SCHEMAS[name]) {
|
||||
const button = $("<button type='button' class='btn btn-sm btn-outline-secondary'></button>")
|
||||
.attr("aria-label", "Credentials for " + name)
|
||||
.html("<i class='fa-solid fa-key'></i> Credentials")
|
||||
.click(() => showCredentialsModal(name));
|
||||
row.append($("<div class='col-auto'></div>").append(button));
|
||||
}
|
||||
container.append(row);
|
||||
});
|
||||
|
||||
if (providers.length > 1) {
|
||||
$("#upstream-provider-label").text("upstream spot sources:");
|
||||
$("#upstream-provider-select-col").show();
|
||||
} else {
|
||||
$("#upstream-provider-label").text(providers[0]);
|
||||
$("#upstream-provider-select-col").hide();
|
||||
}
|
||||
|
||||
// Show the credentials button if this provider has an authentication mechanism and we need input from the user
|
||||
updateCredentialsButton();
|
||||
updateSpotholeOnlyCheck();
|
||||
}
|
||||
|
||||
// Update the credentials button visibility based on selected provider
|
||||
function updateCredentialsButton() {
|
||||
const providerName = getSelectedUpstreamProvider();
|
||||
if (providerName && PROVIDER_CREDENTIAL_SCHEMAS[providerName]) {
|
||||
$("#upstream-credentials-btn").show();
|
||||
} else {
|
||||
$("#upstream-credentials-btn").hide();
|
||||
}
|
||||
// The "Spothole only" checkbox is checked when no upstream providers are selected
|
||||
function updateSpotholeOnlyCheck() {
|
||||
$("#spothole-only").prop("checked", getSelectedUpstreamProviders().length === 0);
|
||||
}
|
||||
|
||||
// Get the currently selected upstream provider name
|
||||
function getSelectedUpstreamProvider() {
|
||||
const providers = SPOT_SUBMIT_PROVIDERS[$("#activity").val()] || [];
|
||||
if (providers.length === 0) return null;
|
||||
if (providers.length === 1) return providers[0];
|
||||
return $("#upstream-provider-select").val();
|
||||
// Get the names of the upstream providers the user has chosen to send the spot to
|
||||
function getSelectedUpstreamProviders() {
|
||||
return $(".upstream-provider-check:checked").map(function () {
|
||||
return $(this).val();
|
||||
}).get();
|
||||
}
|
||||
|
||||
// Show the credentials modal for the currently selected upstream provider
|
||||
function showCredentialsModal() {
|
||||
const providerName = getSelectedUpstreamProvider();
|
||||
if (!providerName || !PROVIDER_CREDENTIAL_SCHEMAS[providerName]) return;
|
||||
// Show the credentials modal for the given upstream provider
|
||||
function showCredentialsModal(providerName) {
|
||||
if (!PROVIDER_CREDENTIAL_SCHEMAS[providerName]) return;
|
||||
|
||||
const schema = PROVIDER_CREDENTIAL_SCHEMAS[providerName];
|
||||
const stored = loadCredentials(providerName);
|
||||
@@ -187,12 +187,13 @@ function addSpot() {
|
||||
}
|
||||
|
||||
// Upstream submission
|
||||
const submitUpstream = $("#submit-upstream").is(":checked");
|
||||
const upstreamProviderName = getSelectedUpstreamProvider();
|
||||
if (submitUpstream && upstreamProviderName) {
|
||||
handling["submit_upstream"] = true;
|
||||
handling["upstream_provider"] = upstreamProviderName;
|
||||
handling["upstream_credentials"] = loadCredentials(upstreamProviderName);
|
||||
const upstreamProviders = getSelectedUpstreamProviders();
|
||||
if (upstreamProviders.length > 0) {
|
||||
handling["upstream_providers"] = upstreamProviders;
|
||||
handling["upstream_credentials"] = {};
|
||||
$.each(upstreamProviders, function (i, name) {
|
||||
handling["upstream_credentials"][name] = loadCredentials(name);
|
||||
});
|
||||
}
|
||||
|
||||
$.ajax("/api/v3/spot", {
|
||||
@@ -262,14 +263,21 @@ $("#mode").change(function () {
|
||||
$(this).val($(this).val().trim().toUpperCase());
|
||||
});
|
||||
|
||||
// Update upstream area and credentials button when activity changes
|
||||
// Update upstream area when activity changes
|
||||
$("#activity").change(function () {
|
||||
updateUpstreamArea();
|
||||
});
|
||||
|
||||
// Update credentials button when provider selector changes
|
||||
$("#upstream-provider-select").change(function () {
|
||||
updateCredentialsButton();
|
||||
// Keep the "Spothole only" checkbox in sync with the upstream provider checkboxes
|
||||
$("#upstream-providers").on("change", ".upstream-provider-check", function () {
|
||||
updateSpotholeOnlyCheck();
|
||||
});
|
||||
|
||||
// Clicking "Spothole only" deselects all upstream providers. It can't be unchecked directly, as that would leave
|
||||
// nowhere to send the spot; the user unchecks it implicitly by choosing an upstream provider instead.
|
||||
$("#spothole-only").change(function () {
|
||||
$(".upstream-provider-check").prop("checked", false);
|
||||
updateSpotholeOnlyCheck();
|
||||
});
|
||||
|
||||
// Startup
|
||||
|
||||
Reference in New Issue
Block a user