Skip to content

GoAdserver: New Adapter - #4648

Open
goadserver wants to merge 3 commits into
prebid:masterfrom
goadserver:goadserver-adapter
Open

goadserver wants to merge 3 commits into
prebid:masterfrom
goadserver:goadserver-adapter

Conversation

@goadserver

Copy link
Copy Markdown

🔧 Type of changes

  • new bid adapter

✨ What's the context?

GoAdserver is a self-hosted, multi-tenant ad server. The Prebid.js adapter (goadserver) is already in Prebid.js, with docs at https://docs.prebid.org/dev-docs/bidders/goadserver.html. This adds the Prebid Server Java adapter. The Go counterpart is prebid/prebid-server#5023.

🧠 Rationale behind the change

  • All requests go to one fixed endpoint, https://pbs.goadserver.com/openrtb2/auction. GoAdserver's gateway routes each request to the publisher's deployment by the publisher token, so the adapter never builds a hostname from request data.
  • Params: token (required), plus optional floor (USD CPM, applied when the imp has no bidfloor) and subid (string or integer, sent as imp.ext.goadserver.subid).
  • Requests: impressions are grouped by token, one request per token, with the token in site.publisher.id. Other publisher fields are kept.
  • Bid type: taken from bid.mtype, falling back to bid.ext.prebid.type, which the endpoint sets on every bid. The endpoint's Prebid.js targeting ext is dropped and bid.ext.dsa is kept for DsaEnforcer.
  • Supported traffic: site only (banner, video, native), because the endpoint needs site.page.
  • No user sync, default OpenRTB 2.5: the sync cookie belongs to each deployment's domain, so there is no single sync URL. The 2.5 downgrade matches what the endpoint reads.

🔎 New Bid Adapter Checklist

  • verify email contact works (support@goadserver.com)
  • NO fully dynamic hostnames: a single fixed endpoint, with routing done server-side by token
  • geographic host parameters are NOT required
  • direct use of HTTP is prohibited: implements Bidder<BidRequest> and uses BidderUtil.defaultRequest
  • if the ORTB is just forwarded to the endpoint, use the generic adapter: not applicable. The adapter groups imps by token, rewrites site.publisher.id and imp.ext, applies the floor param, and filters the response ext.
  • cover an adapter configuration with an integration test (GoadserverTest)

🧪 Test plan

  • GoadserverBidderTest: 18 unit tests covering site-only, grouping by token, publisher override and creation, floor precedence, subid (string and number), missing token, invalid ext, the OpenRTB version header, mtype and ext-type resolution, DSA kept and targeting stripped, unsupported types, and the default currency.
  • GoadserverTest integration test against WireMock.
  • BidderParamValidatorTest and BidderCatalogTest pass, and checkstyle reports 0 violations.
  • The gateway endpoint is live and answers with a valid certificate.

🏎 Quality check

  • Are your changes following our code style guidelines?
  • Are there any breaking changes in your code? (none)
  • Does your test coverage exceed 90%?
  • Are there any erroneous console logs, debuggers or leftover code in your changes? (none)

🤖 Generated with Claude Code

https://claude.ai/code/session_01Logb1f3FNVSzPCTMtqNc8A

Jan and others added 2 commits October 6, 2026 15:58
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Logb1f3FNVSzPCTMtqNc8A
The GoAdserver endpoint accepts OpenRTB app requests (bundle, store url,
device.ifa/lmt, instl, rwdd) on the same gateway, so the bidder now takes a
site or an app: the publisher token goes on app.publisher.id when there is no
site. Bidder config declares app banner, video and native.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@goadserver

Copy link
Copy Markdown
Author

Added app (in-app) support in af61fae, matching the Go adapter (prebid/prebid-server#5023): GoAdserver's endpoint accepts OpenRTB app requests on the same gateway, so the bidder now takes a site or an app and puts the publisher token on app.publisher.id when there is no site. Bidder config declares app banner, video and native. Unit tests for the app path and a second GoadserverTest integration case with an app request.

Comment on lines +41 to +45
/**
* GoAdserver is a self-hosted, multi-tenant ad server. All requests go to one fixed GoAdserver gateway, which
* routes each to the publisher's deployment by the publisher token. Impressions are grouped by token and each
* group is sent with its token in site.publisher.id, or app.publisher.id for in-app requests.
*/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comment

Comment on lines +94 to +96
if (extImp == null) {
throw new PreBidException("imp %s: missing ext.bidder".formatted(imp.getId()));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't be null

return builder.build();
}

// The GoAdserver endpoint reads an optional sub-identifier from imp.ext.goadserver.subid.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove comment

Comment on lines +132 to +138
final BidRequest.BidRequestBuilder builder = request.toBuilder().imp(imps);
if (request.getSite() != null) {
builder.site(modifySite(request.getSite(), token));
} else {
builder.app(modifyApp(request.getApp(), token));
}
final BidRequest outgoingRequest = builder.build();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        final BidRequest outgoingRequest = request.toBuilder()
            .imp(imps)
            .site(modifySite(request.getSite(), token))
            .app(modifyApp(request.getApp(), token))
            .build();
private static Site modifySite(Site site, String token) {
        return site != null
               ? site.toBuilder().publisher(withToken(site.getPublisher(), token)).build()
               : null;
    }

    // same for app

Comment on lines +219 to +220
// Drops the Prebid.js targeting GoAdserver adds for its direct integration and keeps only the
// DSA transparency object, which Prebid Server validates and relays.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comment

Comment on lines +369 to +371
private static UnaryOperator<ExtImpGoadserver> identity() {
return ext -> ext;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???? Use UnaryOperator.identity. You can use static import for it

}

private static BidRequest givenBidRequest(Imp... imps) {
return givenBidRequest(request -> request, imps);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request -> request -> identity()

.page("https://publisher.example/article")
.publisher(Publisher.builder().id("pbs-account").name("Publisher").build())
.build();
return requestCustomizer.apply(BidRequest.builder().id("request-id").site(site).imp(List.of(imps)))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

place apply(...) on new line

.build();
}

private static Imp givenImp(String impId, UnaryOperator<ExtImpGoadserver> extCustomizer) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need customizer for immutable object? Add givenImp(impId) and givenImp(impId, ext) methods instead

Comment on lines +35 to +50
@Test
public void openrtb2AuctionShouldRespondWithBidsFromGoadserverForAppRequest() throws IOException, JSONException {
// given
WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/goadserver-exchange"))
.withRequestBody(equalToJson(jsonFrom("openrtb2/goadserver/test-goadserver-app-bid-request.json")))
.willReturn(aResponse()
.withBody(jsonFrom("openrtb2/goadserver/test-goadserver-bid-response.json"))));

// when
final Response response = responseFor("openrtb2/goadserver/test-auction-goadserver-app-request.json",
Endpoint.openrtb2_auction);

// then
assertJsonEquals("openrtb2/goadserver/test-auction-goadserver-response.json", response,
singletonList("goadserver"));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this test. It's already covered by units

@osulzhenko osulzhenko changed the title New Adapter: GoAdserver GoAdserver: New Adapter Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants