diff --git a/internal/api/api.go b/internal/api/api.go index 6d9a615..d75e4b1 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -74,20 +74,18 @@ func New(services Services) *fiber.App { }) app.Get("/api/v1/diyanet/prayertimes", func(ctx fiber.Ctx) error { - var query struct { - LocationID string `query:"location_id"` - Latitude string `query:"latitude"` - Longitude string `query:"longitude"` - UTC string `query:"utc"` + var params struct { + LocationID string `query:"location_id"` + Latitude *float64 `query:"latitude"` + Longitude *float64 `query:"longitude"` + UTC bool `query:"utc"` } - if err := ctx.Bind().Query(&query); err != nil { + if err := ctx.Bind().Query(¶ms); err != nil { return fmt.Errorf("failed to bind prayer times query parameters: %w", errors.Join(fiber.ErrBadRequest, err)) } - locationID := strings.TrimSpace(query.LocationID) - latitude := strings.TrimSpace(query.Latitude) - longitude := strings.TrimSpace(query.Longitude) - utc := strings.TrimSpace(query.UTC) == "1" + locationID := strings.TrimSpace(params.LocationID) + utc := params.UTC var ( result prayer.TimesResult @@ -97,19 +95,10 @@ func New(services Services) *fiber.App { switch { case locationID != "": result, err = services.PrayerProvider.Get(ctx.Context(), locationID) - case latitude != "" && longitude != "": - lat, latErr := strconv.ParseFloat(latitude, 64) - if latErr != nil { - return fmt.Errorf("failed to parse latitude query parameter: %w", errors.Join(fiber.ErrBadRequest, latErr)) - } - lng, lngErr := strconv.ParseFloat(longitude, 64) - if lngErr != nil { - return fmt.Errorf("failed to parse longitude query parameter: %w", errors.Join(fiber.ErrBadRequest, lngErr)) - } - + case params.Latitude != nil && params.Longitude != nil: result, err = services.PrayerProvider.GetByCoords(ctx.Context(), prayer.Coordinates{ - Latitude: lat, - Longitude: lng, + Latitude: *params.Latitude, + Longitude: *params.Longitude, }) default: return fmt.Errorf("failed to validate prayer times query parameters: %w", fiber.ErrBadRequest) @@ -150,42 +139,29 @@ func New(services Services) *fiber.App { }) app.Get("/api/v1/diyanet/location", func(ctx fiber.Ctx) error { - var query struct { - Text string `query:"query"` - Latitude string `query:"latitude"` - Longitude string `query:"longitude"` + var params struct { + Text *string `query:"query"` + Latitude *float64 `query:"latitude"` + Longitude *float64 `query:"longitude"` } - if err := ctx.Bind().Query(&query); err != nil { + if err := ctx.Bind().Query(¶ms); err != nil { return fmt.Errorf("failed to bind location query parameters: %w", errors.Join(fiber.ErrBadRequest, err)) } - query.Text = strings.TrimSpace(query.Text) - query.Latitude = strings.TrimSpace(query.Latitude) - query.Longitude = strings.TrimSpace(query.Longitude) - var ( locations []prayer.Location err error ) - switch { - case query.Text != "" && query.Latitude == "" && query.Longitude == "": - locations, err = services.LocationProvider.SearchLocations(ctx.Context(), query.Text) - case query.Text == "" && query.Latitude != "" && query.Longitude != "": - lat, latErr := strconv.ParseFloat(query.Latitude, 64) - if latErr != nil { - return fmt.Errorf("failed to parse latitude query parameter: %w", errors.Join(fiber.ErrBadRequest, latErr)) - } - lng, lngErr := strconv.ParseFloat(query.Longitude, 64) - if lngErr != nil { - return fmt.Errorf("failed to parse longitude query parameter: %w", errors.Join(fiber.ErrBadRequest, lngErr)) - } - + if params.Text != nil { + trimmed := strings.TrimSpace(*params.Text) + locations, err = services.LocationProvider.SearchLocations(ctx.Context(), trimmed) + } else if params.Latitude != nil && params.Longitude != nil { locations, err = services.LocationProvider.SearchLocationsByCoords(ctx.Context(), prayer.Coordinates{ - Latitude: lat, - Longitude: lng, + Latitude: *params.Latitude, + Longitude: *params.Longitude, }) - default: + } else { return fmt.Errorf("failed to validate location query parameters: %w", fiber.ErrBadRequest) } if err != nil { @@ -199,12 +175,21 @@ func New(services Services) *fiber.App { }) app.Get("/api/diyanet/search", func(ctx fiber.Ctx) error { - q := strings.TrimSpace(ctx.Query("q")) - if q == "" { + setDeprecationHeaders(ctx, "/api/v1/diyanet/location") + + var params struct { + Query string `query:"q"` + } + if err := ctx.Bind().Query(¶ms); err != nil { + return fmt.Errorf("failed to bind legacy location search query parameters: %w", errors.Join(fiber.ErrBadRequest, err)) + } + + params.Query = strings.TrimSpace(params.Query) + if params.Query == "" { return fmt.Errorf("missing query parameter q: %w", fiber.ErrBadRequest) } - locations, err := services.LegacyLocationProvider.SearchLocations(ctx.Context(), q) + locations, err := services.LegacyLocationProvider.SearchLocations(ctx.Context(), params.Query) if err != nil { return fmt.Errorf("failed to search legacy diyanet locations: %w", err) } @@ -223,9 +208,23 @@ func New(services Services) *fiber.App { return ctx.JSON(resp) }) + app.Get("/api/diyanet/countries", func(ctx fiber.Ctx) error { + return respondDeprecatedEndpoint(ctx, "/api/diyanet/search?q=") + }) + + app.Get("/api/diyanet/countries/:country/cities", func(ctx fiber.Ctx) error { + return respondDeprecatedEndpoint(ctx, "/api/diyanet/search?q=") + }) + + app.Get("/api/diyanet/locations", func(ctx fiber.Ctx) error { + return respondDeprecatedEndpoint(ctx, "/api/diyanet/search?q=") + }) + app.Get("/api/diyanet/prayertimes", func(ctx fiber.Ctx) error { - if services.LegacyLocationProvider == nil { - return fmt.Errorf("legacy location provider is not configured: %w", fiber.ErrNotFound) + setDeprecationHeaders(ctx, "/api/v1/diyanet/prayertimes") + + var params struct { + LocationID int `query:"location_id"` } locationIDText := strings.TrimSpace(ctx.Query("location_id")) @@ -393,3 +392,17 @@ func mapPrayerTimesForLegacyResponse(times []prayer.Times, location prayer.Locat return out, nil } + +func respondDeprecatedEndpoint(ctx fiber.Ctx, replacementPath string) error { + setDeprecationHeaders(ctx, replacementPath) + + return ctx.Status(http.StatusGone).JSON(httpError{ + Message: "This endpoint has been removed. Use " + replacementPath, + }) +} + +func setDeprecationHeaders(ctx fiber.Ctx, replacementPath string) { + ctx.Response().Header.Set("Deprecation", "true") + ctx.Response().Header.Set("Sunset", "Wed, 30 Sep 2026 23:59:59 GMT") + ctx.Response().Header.Set("Link", "<"+replacementPath+">; rel=\"successor-version\"") +}