Added
Steam::webApi()— the base SDK’sWebApiResource, handed back the same way asplayers(),users(),stats(),apps()andnews(), whoseserverTime()returns Steam’s clock as a UTCDateTimeImmutableand never sends the key.supportedApis()sends the key when one is configured, which adds the methods it can call, and goes out without it otherwise, while a key Steam rejects raisesInvalidApiKeyException, rendered as a 500 (#131).WebApiInterfaceFactory,WebApiMethodFactoryandWebApiParameterFactory, withSteamResponse::serverInfo()andSteamResponse::supportedApiList()for the two envelopes.->withoutDescription()drops the description as Steam does on most methods and some parameters, and->emptyDescription()sends the''it sends on other parameters, both read back asnull(#131).SteamResponse::supportedApiListInvalidKey()— the 403 with an empty interface list thatGetSupportedApiListRequestraises asInvalidApiKeyException, faked fluently as->webApi()->supportedApiListInvalidKey()beside->webApi()->serverInfo()and->webApi()->supportedApiList()onSteamFake(#131).PlayerSummaryFactory::friendsOnly()— a profile Steam reports as friends-only, read back asCommunityVisibility::FriendsOnly, which drops whatprivate()drops. It keepscommentpermission, since Steam sends that by the comment setting rather than the visibility (#128).steam-api.rate_limit.limits— leftnullit keeps the base SDK’s 100 000 a day,[]meters nothing locally, and a list of['allow' => …, 'every' => …, 'sleep' => …]entries replaces the default with a key or without,everytaking seconds or'day', which closes at 00:00 UTC on the default’s own counter. A value it cannot take raisesInvalidSteamRateLimitOptionExceptionnaming its key, such assteam-api.rate_limit.limits.1.every, rendered as a 500 (#125).steam-api.rate_limit.cooldown— the seconds a 429 withoutRetry-Afterholds requests back, read fromSTEAM_API_RATE_LIMIT_COOLDOWNand 60 when left unset (#125).php artisan about— a “429 Cool-down” row, marked(default)at 60 (#125).steam-api.http.sender— a class implementingSaloon\Contracts\Sender, read fromSTEAM_API_SENDERand resolved through the container each time the connector is built, so a binding of that class decides how, while leaving it unset keeps Guzzle. A value naming no such class raisesInvalidSteamHttpOptionExceptionnaming the key, rendered as a 500 (#126).php artisan about— a “Sender” row,Guzzle (default)until one is configured, orGuzzle + Nightwatch (default)while Nightwatch is reporting (#126, #127).- Steam requests show up in Laravel Nightwatch’s outgoing requests while
laravel/nightwatchis installed and enabled andsteam-api.http.senderis unset, withsaloonphp/laravel-pluginor without it, since the plugin skips the base SDK’sSteamSender. The API key is stripped from every Steam URL Nightwatch records, while a sender of your own reports only onceNightwatch::guzzleMiddleware()is attached to it (#127).
Changed
- BC break
fkrzski/php-steam-api-sdk^0.9is required, andSteam::connector()->sender()returns itsSteamSender, which wraps the sender in use, soaddMiddleware(),getHandlerStack()andgetGuzzleClient()are gone from it. Middleware added that way, which never outlived the scoped connector anyway, moves onto aGuzzleSenderbound in the container and named insteam-api.http.sender(#124, #126). - BC break
InvalidSteamRateLimitStoreExceptionis nowInvalidSteamRateLimitOptionException, covering the wholesteam-api.rate_limitblock, so code catching it by name has to catch the new class (#125). php artisan aboutreports every local limit in a “Requests Remaining” row, such as99,412 of 100,000 per day, 9 of 10 per 1s (sleeps), where “Daily Requests Remaining” showed only the first, so--jsonoutput moves fromdaily_requests_remainingtorequests_remaining(#125).
Removed
- BC break The 19 endpoint helpers that only forwarded to a resource,
Steam::summaries()throughSteam::appNews(), are gone from the facade,SteamManagerand its contract, whileSteam::newsFeed()stays. Each call moves to its resource with the same arguments and return type, soSteam::groups($id)becomesSteam::users()->groups($id)(#130).
Fixed
- The daily budget resets at 00:00 UTC instead of 24 hours after the first request of its window, and
RespectsSteamRateLimitreleases a job until then. The limit is now named100000_every_utc_midnight, so a counter kept in the rate limit cache store, which outlives a deploy, restarts from zero once, as on the upgrade to 0.4.0 (#124). Steam::users()->summaries()maps a friends-only profile toCommunityVisibility::FriendsOnly, where one such profile among up to 100 IDs threwUnexpectedValueExceptionand failed the whole batch (#124).
Added
Steam::news()— the base SDK’sNewsResource, handed back the same way asplayers(),users(),stats()andapps(). Steam serves its one endpoint anonymously, so it needs noSTEAM_API_KEY(#105).Steam::appNews()— wrapsGetNewsForAppRequestand returnsAppNews, an app’s posts newest first, whosetotalcounts every post the filter matches rather than the page. An app ID Steam does not know and some apps it does, such as Spacewar, both raiseAppNewsUnavailableException, because Steam answers the two alike (#105).AppNewsFactoryandNewsItemFactory, withSteamResponse::appNews()for theappnewsenvelope.->total()keeps Steam’s count apart from the items listed, so a test can fake page one of many, and->withoutAuthor()sends the''Steam sends for a post without a byline, which the DTO reads back asnull(#105).SteamResponse::appNewsUnavailable()— the 403 with an empty JSON object thatGetNewsForAppRequestraises asAppNewsUnavailableException, faked fluently as->news()->appNewsUnavailable()beside->news()->appNews()onSteamFake(#105).Steam::newsFeed()— an app’s news as aLazyCollectionofNewsItem, oneGetNewsForAppRequestper page ofperPage(20), skipping by ID the items each inclusiveenddaterepeats, so->take(30)stops sending once it has enough. It ends on a page holding all that remains or bringing nothing new, which cuts it short whenperPageor more items share one second, and aperPagebelow 2 raisesInvalidArgumentException(#109).SteamResponse::newsFeed()— a whole feed ofNewsItemFactoryitems, answering eachGetNewsForAppRequestwith the page Steam would send for itscountandenddate, faked fluently as->news()->newsFeed()onSteamFake.feeds,tagsandmaxlengthare not applied (#109).Events\SteamRequestSendingandEvents\SteamResponseReceived, dispatched for every attempt with the Steammethod, the key-freequeryand theattempt, the second addingstatusanddurationin seconds, andEvents\SteamRequestFailed, dispatched once per failed call with theexceptionthe caller gets. Listeners sit on the dispatcher, so they outlive the scoped connector andEvent::fake()sees them underSteam::fake(), but that exception does not serialize, so aSteamRequestFailedlistener cannot be queued (#107).steam-api.logging— off by default,channel(STEAM_API_LOG_CHANNEL) logs every failed Steam call there as a warning with the exception’s class and code in context, andresponses(STEAM_API_LOG_RESPONSES) adds every response at debug with its query, Steam IDs included, in context. Both run as listeners on the traffic events, soEvent::fake()silences them, while a channellogging.channelsdoes not define or aresponsesthat is not a boolean raisesInvalidSteamLoggingOptionException, a 500 likeInvalidSteamRateLimitStoreException(#108).php artisan about— a “Logging” row readingdisabled, the channel name, with(+ responses)once responses are logged too, orINVALIDfor a rejected option (#108).
Changed
- BC break
fkrzski/php-steam-api-sdk^0.8is required, and every message built from a Steam response opens with the Steam method, rendered bodies included, so an unknown app’s 404 readsGetNumberOfCurrentPlayers: No Steam app found for app ID 1.A client matching onmessagehas to match the new text, while the 429, 500 and 503 bodies stay as they were (#104). - BC break
Steam::send()andSteam::pool()raise the failure Steam reports in a 200 payload inside the call, soSteam::send(new GetOwnedGamesRequest(...))for a private profile throws there rather than on->dto(), and a pool rejects where it used to fulfil. The helpers and the resources throw what they did before (#104). - BC break
getPrevious()is null onSteamConnectionExceptionand on aSteamApiExceptionraised from a 4xx or 5xx, so areport()callback walking the chain for Saloon’s exception finds nothing. Read the status fromgetCode()and the payload fromresponseinstead (#104). - BC break
ProfileNotPublicException::forSteamId(),ProfileNotPublicException::forPrivateOrMissing()andSteamUserNotFoundException::forVanity()require the SaloonResponse, so code building them by hand has to pass one (#104). AppNewsUnavailableExceptionrenders as a 404 rather than a blanket 500, and not as the 403 Steam answered with, since the client asked for nothing forbidden. It joins the client-facing group understeam-api.exceptions.render, so an application rendering it itself is unaffected (#106).
Fixed
Steam::connector()->debug()andSteamConnectionExceptionmask the API key askey=***, andSteam::pool()counts a 4xx or 5xx against the daily budget and raises a 429 asSteamRateLimitException, the same as a helper call (#104).Steam::fake()types a callable response as taking thePendingRequestSaloon passes it, so a closure reading the request no longer fails static analysis (#109).
Added
Steam::apps()— the base SDK’sAppsResource, handed back the same way asplayers(),users()andstats(). Steam serves every ISteamApps endpoint anonymously, so none of them needsSTEAM_API_KEY(#86).Steam::upToDateCheck()— wrapsUpToDateCheckRequestand returnsAppVersionCheck, telling a game server whether its version is current. An app ID Steam does not know and an app running no versioned servers both raiseAppVersionUnavailableException, because Steam answers the two alike (#86).Steam::serversAtAddress()— wrapsGetServersAtAddressRequestand returnslist<GameServer>, the game servers at an IP address. An address Steam rejects raisesInvalidServerAddressException(#86).AppVersionCheckFactoryandGameServerFactory, withSteamResponse::upToDateCheck()andSteamResponse::serversAtAddress()for theirresponseenvelopes.->withoutSpectatorPort()sends the0Steam sends for a server without SourceTV, which the DTO reads back asnull(#86).SteamResponse::appVersionUnavailable(),SteamResponse::invalidServerAddress()andSteamResponse::serversAtAddressRefused()— the three failures the new endpoints answer with 200 andsuccess: false. The last is the per-IP refusal the base SDK raises as the rootSteamApiException, so nothing but the builder names it (#86).Steam::sdrConfig()— wrapsGetSdrConfigRequestand returnsSdrConfig, the Steam Datagram Relay points of presence a game connects through, keyed by code. An app ID Steam does not know raisesAppNotFoundExceptionon the first attempt, however many triessteam-api.http.retry.triesallows (#89).SdrConfigFactory,SdrPointOfPresenceFactoryandSdrRelayFactory, withSteamResponse::sdrConfig()for the top-level payload. A point of presence carries its code into the key ofpopsrather than the payload,->latitude()and->longitude()writegeoin Valve’s[longitude, latitude]order, and->withoutRelays()drops the key the way Steam does (#89).SteamResponse::sdrConfigAppNotFound()— the 500 carryingFailed to get appinfothatGetSdrConfigRequestraises asAppNotFoundException, a third unknown-app shape besideappNotFound()andschemaAppNotFound()(#89).steam-api.http—connect_timeoutandrequest_timeoutin seconds, andretry.tries,retry.intervalin milliseconds andretry.exponential_backoff, each read from its ownSTEAM_API_*variable and handed to the base SDK’sSteamConfigwhen the connector is built. Left unset they keep Saloon’s 10 and 30 seconds and a single attempt, while a value that is not a number or is out of range raisesInvalidSteamHttpOptionExceptionnaming the key — a misconfiguration, so it renders as a 500 whateversteam-api.exceptions.rendersays (#87).php artisan about— “Timeouts” and “Retries” rows reporting what the connector is built with, marked(default)until thesteam-api.httpblock changes them. A rejected value readsINVALIDin its own row, and the daily budgetUNKNOWNwhenever any row does, since the connector cannot be built (#87).Steam::fake()drops the pause between attempts but keeps the tries, so a faked 5xx is retried at once andSteam::assertSentCount()still counts every attempt. Saloon pauses with a bareusleep(), whichSleep::fake()cannot reach (#87).Queue\Middleware\RespectsSteamRateLimit— job middleware for a job’s ownmiddleware(), releasing it for what is left of the window when the daily budget is already spent, before the job runs, or when a request inside it raisesSteamRateLimitException; without an API key only a 429 from Steam holds it back. A release counts as an attempt, so a job waiting out a daily window wantsretryUntil()rather than a small$tries(#88).steam-api.rate_limit.store— the cache store the rate limit counter is kept in, read fromSTEAM_API_RATE_LIMIT_STORE, so acache:clearof the default store no longer resets the daily budget while Steam’s own count carries on. Left unset it stays the default store, and a namecache.storesdoes not define raisesInvalidSteamRateLimitStoreException, a 500 likeInvalidSteamHttpOptionException(#96).SteamResponse::connectionFailed()— a request that never reaches Steam, retried like a real outage and raised asSteamConnectionExceptiononce the tries are spent. Saloon throws it before recording a response, soSteam::recorded()and theassertSentfamily never see the attempt (#90).Steam::fake()->users()->summaries(...)and the rest of the four resources’ endpoints on the returnedSteamFake, each registering itsSteamResponsebuilder under the request class it sends and handing the mock back, so a chain moves resource by resource and still ends on something the assertions read. A second call to the same endpoint replaces the first (#91).- Named failures beside each endpoint —
->users()->friendsNotPublic(),->stats()->schemaAppNotFound()and the rest — each faking what Steam answers that endpoint with, so the test gets the exception the base SDK raises for it.->invalidApiKey()and->connectionFailed()onSteamFakeanswer every request without a response of its own (#91).
Changed
- BC break
fkrzski/php-steam-api-sdk^0.7is required, and a transport failure surfaces as itsSteamConnectionExceptionrather than Saloon’sFatalRequestException. Code catching the Saloon exception has to catch the SDK one instead (#85). php artisan aboutreadsnot metered (no API key)for the daily budget with no key configured, where it counted down from 100 000 before. Steam bills the quota to the key, so a connector built without one meters nothing (#85).php artisan aboutnames the configured store in its “Rate Limit Store” row and marks the default one(default), where it always readcache.defaultbefore. A store the cache config does not define readsINVALID(#96).SteamConnectionException, and the rootSteamApiExceptionraised for a 5xx once the retries are spent, render as a 503 rather than a blanket 500, its body sayingService Unavailablebecause base 0.7.0 quotes the request URI, API key included, in the message (#90).AppVersionUnavailableExceptionrenders as a 404 andInvalidServerAddressExceptionas a 422, rather than a blanket 500. Both join the 503 in the client-facing group understeam-api.exceptions.render, so an application rendering these itself is unaffected (#90).- BC break
Contracts\SteamManager::fake()returnsTesting\SteamFake, a subclass of Saloon’sMockClient, so a class implementing the contract itself has to narrow its return type. The array form and every assertion on the returned mock work as before (#91).
Removed
- BC break
SteamApiKeyMissingExceptionandHttp\RequiresConfiguredApiKey— the base SDK refuses a request that needs a key before it goes out, raisingApiKeyNotConfiguredException, which renders as a 500 the same way. Code catching the bridge exception has to catch the base one instead (#85).
0.6.0
Added
Steam::currentPlayers()— wrapsGetNumberOfCurrentPlayersRequestand returns the concurrent player count as a plainintrather than a DTO. An app ID Steam does not know raisesAppNotFoundException(#70).Steam::globalAchievements()— wrapsGetGlobalAchievementPercentagesForAppRequestand returnslist<GlobalAchievement>, one entry per achievement with the share of the player base that has it. Its argument is$gameId, the name the base SDK keeps because Valve spells this one endpointgameid(#70).GlobalAchievementFactory, withSteamResponse::currentPlayers()andSteamResponse::globalAchievements()for their envelopes. The factory holds the percentage as the string Steam sends, so the cast to a float stays the DTO’s (#70).SteamResponse::appNotFound()andSteamResponse::globalAchievementsRefused()— the two failure shapes the new endpoints answer with, a 404 carryingresult: 42and a 403 carrying an empty JSON object. Neither could be built from the failure builders already here (#70).Steam::schema()— wrapsGetSchemaForGameRequestand returnsGameSchema, every stat and achievement a game publishes. An app publishing no schema answers with an empty DTO rather than a failure, while an app ID Steam does not know raisesAppNotFoundException(#71).GameSchemaFactory,SchemaStatFactoryandSchemaAchievementFactory, withSteamResponse::gameSchema()for thegameenvelope.->empty()builds what an app publishing no schema answers with, dropping the name and version along with both lists (#71).SteamResponse::schemaAppNotFound()— the bare 400GetSchemaForGameRequestraisesAppNotFoundExceptionfrom. It returns byte for byte whatSteamResponse::statsRefused()does, and keeps its own name because nothing but the endpoint says which failure a test is faking (#71).steam-api.language— the default language every localised request carries, read fromSTEAM_API_LANGUAGEand validated against the base SDK’sLanguageenum when the connector is built. It ships unset and an empty string sends no language at all, while a code Steam does not know raisesInvalidSteamLanguageException— a misconfiguration, so it renders as a 500 whateversteam-api.exceptions.rendersays (#72).php artisan about— a “Language” row reporting the code every localised request carries and which of the two answers it is,polish (config)orpolish (locale: pl).NOT SETnames its source the same way, whileINVALIDstands alone, since only config can carry a rejected code (#72, #73).Contracts\SteamLanguageResolver, bound toLocalization\LocaleLanguageResolver— withsteam-api.languageunset the language followsapp()->getLocale()through a table of Valve’s own codes, soplispolishwhilekoiskoreanaandpt_BRisbrazilian. A locale Steam publishes no language for sends none rather than raising, and an application with its own locale scheme rebinds the contract instead of forking the map (#73).AppNotFoundExceptionrenders as a 404 — an app ID Steam does not know is a missing resource rather than a server fault, andSteam::currentPlayers()andSteam::schema()both raise it. It joins the client-facing group understeam-api.exceptions.render, so an application rendering these itself is unaffected (#74).
Changed
- BC break
fkrzski/php-steam-api-sdk^0.6is required, and the$languageargument onSteam::userStats()andSteam::achievements()is aLanguageenum rather than a string. A call passing'english'swaps it forLanguage::English(#69). php artisan aboutreports the daily request budget with no key configured, where it readUNKNOWNbefore. The counter hangs off the connector, which is now built without a key, and anonymous requests spend it like any other (#75).
Fixed
Steam::currentPlayers()andSteam::globalAchievements()no longer needSTEAM_API_KEYset, because the key is checked when a request that needs one is sent rather than while the connector is built. Steam serves both endpoints anonymously, and a request that does need a key still fails before it goes out, withSteamApiKeyMissingExceptionnaming the config value to set (#75).
0.5.0
Added
Steam::recentlyPlayedGames()— wrapsGetRecentlyPlayedGamesRequestand returnsRecentlyPlayedGames. Its$totalCountis Steam’s own total for the two-week window, so it can outrun the games the payload lists (#56).Steam::steamLevel()— wrapsGetSteamLevelRequestand returns the community level as a plainintrather than a DTO (#56).RecentlyPlayedGamesFactoryandRecentlyPlayedGameFactory, withSteamResponse::recentlyPlayedGames()andSteamResponse::steamLevel()for their envelopes.->totalCount()sets the total apart from the games listed,->nothingPlayed()builds the window a player played nothing in (#56).Steam::badges()— wrapsGetBadgesRequestand returnsPlayerBadges, the badges a player has earned alongside the level and XP they add up to. A hidden profile raisesProfileNotPublicExceptionrather than returning an empty list (#57).Steam::communityBadgeProgress()— wrapsGetCommunityBadgeProgressRequestand returnslist<CommunityBadgeQuest>, one entry per quest with the flag saying whether it is done (#57).BadgeFactory,PlayerBadgesFactoryandCommunityBadgeQuestFactory, withSteamResponse::badges()andSteamResponse::communityBadgeProgress()for the envelopes. Two named states cover what the badge payload does that no other does:->communityItem()for the id Steam sends as a string,->withoutApp()for a badge tied to no game (#57).SteamResponse::apiKeyUnauthorized()— the same rejected keyinvalidApiKey()fakes, answered with 401 instead of 403.GetCommunityBadgeProgresspicks that status, so without this builder the mapping toInvalidApiKeyExceptionhad nothing to test against (#57).Steam::players(),Steam::users()andSteam::stats()— the base SDK’s fluent resources, handed back exactly as the connector builds them. A resource reached this way sends through the connectorSteam::fake()attached its mock to, so one fake covers both the fluent and the flat surface (#58).Contracts\SteamManagerandContracts\SteamIdBinder— the interfaces theSteamfacade and the route binding resolve, so injecting or swapping either piece means binding an interface rather than type-hinting afinalclass. The concrete classes stay bound under their own names, so an application that already reboundSteamIdRouteBindingkeeps winning (#59).
Changed
- BC break
fkrzski/php-steam-api-sdk^0.5is required, andPlayerSummary::$timeCreatedis nullable there because Steam omits the creation date on a hidden profile. Code reading the date offSteam::summaries()needs a null check (#55). - BC break
SteamResponse::ownedGamesNotPublic()is nowSteamResponse::playerServiceNotPublic(), because every IPlayerService endpoint refuses a hidden profile with the same emptyresponseobject. Tests calling the old name have to rename (#56). - BC break Six facade helpers carry the names of the resource methods they now delegate to:
playerSummaries()issummaries(),playerBans()isbans(),friendList()isfriends(),userGroupList()isgroups(),userStatsForGame()isuserStats()andplayerAchievements()isachievements(). Calling code has to rename; theSteamResponsebuilders keep their own names, which follow the request classes they fake rather than the helpers (#58).
Fixed
PlayerSummaryFactory::private()dropstimecreatedalong with the rest of the details Steam withholds, so a faked hidden profile carries a nulltimeCreatedthe way the real response does. A test asserting the faked timestamp has to drop that assertion (#60).
0.4.0
Added
Steam::playerBans()— wrapsGetPlayerBansRequestand returnslist<PlayerBan>for a batch of up to 100 IDs. Ban records are public, so a hidden profile still returns a row (#35).Steam::userGroupList()— wrapsGetUserGroupListRequestand returnslist<UserGroup>, each carrying the group’sgid. A hidden profile raisesProfileNotPublicExceptionrather than returning an empty list (#35).php artisan about— a “Route Binding” row reportingdisabled, orenabledwith the route parameter the binding claims (#41).- DTO factories in
Fkrzski\LaravelSteamApiSdk\Testing\Factories— one for every DTO the facade returns, building the raw Steam payload withtoArray()or the DTO itself withmake(). Named states cover the variations worth naming:->private(),->online(),->vacBanned(),->locked()(#36). SteamResponse— builds aMockResponsefor every endpoint the facade wraps, filled from the DTO factories. No two endpoints nest their collection alike, and the failure builders cover the three separate shapes Steam refuses a request with (#36).Steam::assertSent(),assertNotSent(),assertNothingSent(),assertSentCount()andassertSentInOrder()— Saloon’sMockClientassertions on the facade, so a test no longer has to hold the mockSteam::fake()returns. Asserting without a fake raisesFakeNotInstalledExceptionrather than passing on an empty history (#38).Steam::recorded(),Steam::lastRequest()andSteam::lastResponse()— Saloon’sMockClientreaders on the facade, so a test that inspects the traffic rather than asserting on it no longer has to hold the mock either. Reading without a fake raisesFakeNotInstalledException, same as the assertions (#51).- An unhandled Steam failure renders as the status it maps to rather than a blanket 500: 404 for
SteamUserNotFoundExceptionandStatsUnavailableException, 403 forProfileNotPublicException, 429 withRetry-AfterforSteamRateLimitException, and 500 for a misconfiguration — never the status Steam answered a rejected key with. Setsteam-api.exceptions.rendertofalseto render the first three groups yourself; the 500 group is not configurable (#37).
Changed
- BC break The
{steamId}route binding is opt-in —steam-api.route_binding.enableddefaults tofalse, in the shipped config and in the provider’s fallback alike. Applications that relied on the old default have to set it totrue(#41). - BC break
fkrzski/php-steam-api-sdk^0.4is required, and it raises every HTTP failure as an SDK exception instead of a SaloonRequestExceptionsubclass. Code catching Saloon’sUnauthorizedExceptionfor a private profile has to catchProfileNotPublicException— or the rootSteamApiException— instead (#34). - BC break
AsSteamIdimplementsSerializesCastableAttributes, sotoArray()— and every API Resource built on it — emits the plain 64-bit string instead of theSteamIdvalue object. Code reading$model->toArray()['steam_id']as an object has to read the attribute directly instead;toJson()already emitted the string, and now matches (#39). - The daily rate-limit counter is keyed by API key rather than by connector class, so the budget
php artisan aboutreports starts from a fresh count after the upgrade. Only the cached counter is orphaned — the quota Steam enforces is untouched.
Fixed
AsSteamIdaccepts anintor anyStringableon write, alongside astringand aSteamId, and rejects the rest withInvalidSteamIdExceptionrather than aTypeErrorfrom inside the model. A JSON body sending the ID as an integer, and$request->string(), no longer need converting by hand (#42).
0.3.0
Added
Steam::friendList()— wrapsGetFriendListRequestand returnslist<Friend>, optionally narrowed to a singleFriendRelationship. A private friend list is a401from Steam, so the call throws Saloon’sUnauthorizedExceptionrather than returning an empty list.php artisan about— a “Steam API” section with the masked API key (last four characters only), the cache store backing the rate limit, and the remaining daily request budget. Values are resolved when the command renders, never on boot.php artisan steam:install— publishesconfig/steam-api.phpand appends the promptedSTEAM_API_KEYto.env. Safe to re-run: an existing config file is kept, and an existing key is only replaced once confirmed.
Changed
- BC break Every class the package ships is now
final, so a subclass ofSteamManager,SteamIdRule,AsSteamIdorSteamServiceProviderno longer compiles. The documented extension points go through container rebinding —SteamIdRouteBinding, theSteamConnectorbinding — not inheritance. - BC break
fkrzski/php-steam-api-sdk^0.3is required, and it groups its request classes into per-interface subnamespaces —Http\Requests\ISteamUser,Http\Requests\ISteamUserStatsandHttp\Requests\IPlayerService. The facade helpers are unaffected, but code that names a request class directly — aSteam::fake()response map,assertSent(), or a request passed tosend()orpool()— has to update its imports. SteamConnectorandSteamManagerare bound as scoped instances rather than singletons, so both are rebuilt per request. A connector held beyond the request that resolved it is now stale — on a long-lived worker the shared one carried its API key into every later request.Steam::fake()throwsFakeOutsideTestsExceptionunless the application environment istesting. Nothing detaches the mock once it is attached.
Fixed
Steam::fake()no longer leaks itsMockClientinto later requests. The manager resolved the connector from the container captured at registration rather than the one that built it, so under Octane the mock stayed attached to a connector shared by the whole worker.- A missing Steam Web API key throws
SteamApiKeyMissingExceptionnaming both theSTEAM_API_KEYenv var and thesteam-api.keyconfig value, instead of surfacing aTypeErrorfromSteamConfiginside the container. The check runs when the connector is first built rather than at boot, so an application without a key can still boot and publish the config.
0.2.0
Added
AsSteamIdEloquent cast — converts a model attribute to aSteamIdvalue object on read and serializes it back to its 64-bit string on write. Values are validated throughSteamId::fromSteamId64; non-scalar stored values throwInvalidSteamIdExceptionandnullis preserved.SteamIdroute binding — a{steamId}route parameter is resolved into aSteamIdvalue object throughSteamId::tryFromInput(accepting a 64-bit ID or a/profiles/<id>URL), aborting with a 404 on unresolvable input. Enabled by default and configurable viasteam-api.route_binding(enabled,parameter); the resolverSteamIdRouteBindingis resolved from the container and can be swapped by rebinding it.SteamIdRulevalidation rule — validates that an attribute resolves to aSteamId, accepting a 64-bit ID or a/profiles/<id>URL by default and only a raw 64-bit ID instrict()mode. Validation is format-only, and its messages ship as thesteam-apitranslation namespace, publishable under thesteam-api-translationstag.
0.1.0
Added
SteamServiceProvider— auto-discovered; bindsSteamConnectoras a singleton (Octane-safe resolver) wired with the configured API key and the Laravel cache rate-limit store. Publishesconfig/steam-api.phpunder thesteam-api-configtag.SteamManager— thin wrapper overSteamConnectorexposingconnector(),send(),pool()and convenience methods (playerSummaries(),ownedGames(),userStatsForGame(),playerAchievements(),resolveVanityUrl()).Steamfacade for static access to the manager.Steam::fake()— attaches a SaloonMockClientto the singleton connector and returns it for assertions, removing per-test connector wiring.