Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor of Kargo Prebid adapter. #11

Merged
merged 29 commits into from
Feb 9, 2023
Merged

Refactor of Kargo Prebid adapter. #11

merged 29 commits into from
Feb 9, 2023

Conversation

njflynn
Copy link

@njflynn njflynn commented Dec 6, 2022

Type of change

  • Refactoring (no functional changes, no api changes)

Description of change

Refactoring of existing adapter to clean up code and send a smaller payload to the server

Copy link

@jsadwith jsadwith left a comment

Choose a reason for hiding this comment

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

Hey Neil - thanks for working on this. I have some initial notes to get started. Keep in mind that the unit testing will need to be updated to support the new data formats.

@andyrusiecki you had mentioned (or was it Julian) that it'd be good to update some of the coding style in here to be more modern. Any ideas you can share?

modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
Copy link

@ssadman22 ssadman22 left a comment

Choose a reason for hiding this comment

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

looks nice, existing comments

modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
Copy link

@juliangan07 juliangan07 left a comment

Choose a reason for hiding this comment

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

mostly things that are already mentioned.

modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
Copy link

@ssadman22 ssadman22 left a comment

Choose a reason for hiding this comment

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

Looks good so far, minor things and comments above

const SUA_BID_KEY = 'ortb2.device.sua'
const SUA_ATTRIBUTES = ['browsers', 'platform', 'mobile', 'model', 'source']
const MOBILE = 'mobile'
const MODEL = 'model'

Choose a reason for hiding this comment

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

+1 on the above

There's a lot of global variables here, some that are just used once. Consider using local variables instead
Ref: https://www.w3schools.com/js/js_best_practices.asp

}, spec._getAllMetadata(bidderRequest, tdid));
imp: impressions,
user: spec._getUserIds(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent),
eids: firstBidRequest.userIdAsEids

Choose a reason for hiding this comment

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

Should eids be nested within user?

"user": {
        "kid": "9449b364-1e40-ff76-a2f1-e9a3caafe1c0", // Kargo ID 
        "cid": "de979bd7-73b5-4785-913e-baf079872c20", // Kargo client ID
        "crbids": {}, // Cerberus IDs
        "tdid": "ed1562d5-e52b-406f-8e65-e5ab3ed5583c", // Trade Desk ID
        "usp": "1---",
        "gdpr": {
            "consent": "",
            "applies": false
        },
        "eids": { // 3rd party user info from userIdAsEids
            [ 
                {

Ref: https://kargo1.atlassian.net/wiki/spaces/KRAK/pages/3171876871/Prebid.js+Adapter+-+Request+Format+Proposal

url: `${HOST}/api/v2/bid`,
data: `json=${encodedParams}`,
method: 'POST',
url: `${HOST}/api/v1/prebid`,

Choose a reason for hiding this comment

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

Nit: probably not necessary but should the entire route just be a const?

Copy link
Author

Choose a reason for hiding this comment

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

In the latest changes I've added a "HOST" and "REQUEST_ENDPOINT" consts as part of a BIDDER object. That would allow for future reusability of both individually, should the need arise.

Comment on lines 416 to 422
if (bid.ortb2Imp.ext.data.pbAdSlot != null && bid.ortb2Imp.ext.data.pbAdSlot != '') {
return bid.ortb2Imp.ext.data.pbAdSlot
}

if (bid.ortb2Imp.ext.data.pbAdSlot != null && bid.ortb2Imp.ext.data.pbAdSlot != '') {
return bid.ortb2Imp.ext.data.pbAdSlot
}

Choose a reason for hiding this comment

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

This looks like its duplicated

Copy link
Author

Choose a reason for hiding this comment

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

Yep, nice spot

function interpretResponse(response, bidRequest) {
let bids = response.body;
const bidResponses = [];
for (let bidId in bids) {

Choose a reason for hiding this comment

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

I think we can refactor this to use ES6's Object.entries. i.e:

  if (isEmpty(bids)) {
    return bidResponses;
  }

  if (typeof bids !== 'object') {
    return bidResponses;
  }

  Object.entries(bids).forEach((entry) => {
    const [bidID, adUnit] = entry
    let meta = {
      mediaType: adUnit.mediaType && BIDDER.SUPPORTED_MEDIA_TYPES.includes(adUnit.mediaType) ? adUnit.mediaType : BANNER,
    };

    if (adUnit.metadata && adUnit.metadata.landingPageDomain) {
      meta.clickUrl = adUnit.metadata.landingPageDomain[0];
      meta.advertiserDomains = adUnit.metadata.landingPageDomain;
    }

    const bidResponse = {
      requestId: bidID,
      cpm: Number(adUnit.cpm),
      width: adUnit.width,
      height: adUnit.height,
      ttl: 300,
      creativeId: adUnit.id,
      dealId: adUnit.targetingCustom,
      netRevenue: true,
      currency: adUnit.currency || bidRequest.currency,
      mediaType: meta.mediaType,
      meta: meta
    };

    if (meta.mediaType == VIDEO) {
      bidResponse.vastXml = adUnit.adm;
    } else {
      bidResponse.ad = adUnit.adm;
    }

    bidResponses.push(bidResponse);
  })

modules/kargoBidAdapter.js Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved

function getUserSyncs(syncOptions, responses, gdprConsent, usPrivacy) {
const syncs = [];
const seed = spec._generateRandomUuid();

Choose a reason for hiding this comment

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

I think these can't reference spec anymore since it's now a function on the file itself and not part of the spec object

Suggested change
const seed = spec._generateRandomUuid();
const seed = _generateRandomUuid();

Copy link
Author

Choose a reason for hiding this comment

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

I have removed this reference where I can. Some need to be maintain it to ensure unit test stubbing works correctly

return {};
}
},
function _getCrbFromLocalStorage() {

Choose a reason for hiding this comment

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

Now that these are functions declared in the file, can probably omit the usages of _<functionName>.

Suggested change
function _getCrbFromLocalStorage() {
function getCrbFromLocalStorage() {

Copy link
Author

Choose a reason for hiding this comment

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

I have done this for all non-exported functions. Functions that are exported in 'spec' object that are not expected still carry this prefix to distinguish them. Happy to remove from all if you think so

modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Show resolved Hide resolved
Copy link

@juliangan07 juliangan07 left a comment

Choose a reason for hiding this comment

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

Looks good, just a suggestion.

function interpretResponse(response, bidRequest) {
let bids = response.body;
const bidResponses = [];
for (let bidID in bids) {

Choose a reason for hiding this comment

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

Consider using Object.keys().forEach() or Object.entries() over a for in loop for some performance gain and modernizing this to use ES6.

Copy link
Author

Choose a reason for hiding this comment

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

Yep, sorry I overlooked this in your previous review. Made that change now

Copy link

@fionasequeira fionasequeira left a comment

Choose a reason for hiding this comment

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

thanks for all the work here @njflynn was a good source of learning and understanding the adapter!

Copy link

@fionasequeira fionasequeira left a comment

Choose a reason for hiding this comment

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

just couple of questions i had when going through this PR

modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Show resolved Hide resolved
modules/kargoBidAdapter.js Show resolved Hide resolved
Copy link

@ssadman22 ssadman22 left a comment

Choose a reason for hiding this comment

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

This looks great! Nice work taking care of such a large refactor. Just a few comments


if (adUnit.mediaType && SUPPORTED_MEDIA_TYPES.includes(adUnit.mediaType)) {
meta.mediaType = adUnit.mediaType;
for (const suaKey of SUA_ATTRIBUTES) {

Choose a reason for hiding this comment

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

Optional nit, but ES6 syntax:

SUA_ATTRIBUTES.forEach(suaKey => {
      const suaValue = uaClientHints[suaKey]
      if(!suaValue) {
        continue
      }

      // Do not pass any empty strings
      if(typeof suaValue == 'string' && suaValue.trim() === '') {
        continue
        
      switch (suaKey) {
        case SUA.MOBILE, SUA.SOURCE:
          if (suaValue < 1) {
            return;
          };
        default:
          suaValidAttributes.push(suaKey);
      }
});

modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
modules/kargoBidAdapter.js Show resolved Hide resolved
Comment on lines 251 to 255
timeoutData.forEach((bid) => {
sendTimeoutData(bid.auctionId, bid.timeout);

});
}

Choose a reason for hiding this comment

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

Nit: extra line

Suggested change
timeoutData.forEach((bid) => {
sendTimeoutData(bid.auctionId, bid.timeout);
});
}
timeoutData.forEach((bid) => {
sendTimeoutData(bid.auctionId, bid.timeout);
});
}

});
}

function _generateRandomUUID() {

Choose a reason for hiding this comment

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

Should this be generateRandomUUID? Same with _getCrb and _getSessionId

Choose a reason for hiding this comment

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

Just read your comment

I have done this for all non-exported functions. Functions that are exported in 'spec' object that are not expected still carry this prefix to distinguish them. Happy to remove from all if you think so

Feel free to disregard :)

Copy link

@jsadwith jsadwith left a comment

Choose a reason for hiding this comment

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

Thanks for all the work on this @njflynn. I'm running some local tests on this today to compare that we're getting the same data we were previously getting. Will keep you updated.

modules/kargoBidAdapter.js Show resolved Hide resolved
modules/kargoBidAdapter.js Outdated Show resolved Hide resolved
Copy link

@jsadwith jsadwith left a comment

Choose a reason for hiding this comment

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

@njflynn I tested both banner and video and things are looking good. I made some minor changes in the code, but all data is being passed as expected! Great work on this! Once the server-side is deployed, we can do some actual test requests with this and if all looks good, we can get it tested on some Fabrik sites (like Distractify) where we control the Prebid.js integrations.

@njflynn njflynn merged commit 1102cfa into master Feb 9, 2023
juliangan07 pushed a commit that referenced this pull request Jul 10, 2023
sj1815 pushed a commit that referenced this pull request Feb 27, 2024
* PE-87: Implement Prebid Adapter (#1)

* PE-87: implement BT Bid Adapter

* PE-87: rework adapter to use ortbConverter lib, make requested changes

* PE-87: update imports

* PE-110: Add user sync logic to the Prebid Adapter (#3)

* PE-110: add user sync logic

* PE-110: update userSync url

* PE-110: check if iframe is enabled before setting params

* PE-111: BT Prebid Adapter can request AA ads or regular ads (#2)

* PE-120: Send Prebid Bidder info to BT Server (#4)

* PE-120: add btBidderCode to the bid object

* PE-120: use single quotes for logs string

* PE-123: Add More Metadata in site.ext.blockthrough (#5)

* PE-123: send additional meta data

* PE-123: send auctionID under imp.ext.prebid.blockthrough

* PE-123: use ortb2 config to set site.ext params

* PE-123: sent auctionId in ext.prebid.blockthrough.auctionID

* PE-123: update logs for bidderConfig setup

* PE-000: check if blockthrough is defined (#6)

* PE-87: remove BT specific logic (#7)

* Implement Blockthrough Prebid Adapter

* PE-87: Implement Prebid Adapter - misc fixes (#9)

* PE-87: rename test file, add bidder config

* PE-87: increase ttl

* PE-000: fix test

* BP-74: Change the way we enable debug (#10)

* BP-79: Send GPID as a part of `imp[].ext` (#11)

* BP-79: send gpid in imp.ext

* BP-79: add optional operator

* BP-90: Update Cookie Sync Logic (#12)

* BP-90: pass bidder to cookie sync

* BP-90: update sync logic, fix typo

* BP-90: use const for syncs variable

* BP-55: Re-add endpoint URLs (#13)

* BP-91: Add prebid JS version to auction request (#14)
nickllerandi pushed a commit that referenced this pull request Mar 21, 2024
* create setupadBidAdapter

* add setupadBidAdapter

* update setupadBidAdapter

* update metrics collection

* update analytics collection

* update getUserSyncs

* add setupadAnalyticsAdapter.js

* test setupadAnalyticsAdapter

* remove test: 1

* add GVLID && bug fixes && test updates

* remove setupadAnalyticsAdapter

* add userID module handling

* add GVLID && bug fixes && test updates

* remove setupadAnalyticsAdapter

* add userID module handling

* clean up && seat bugfix

* clean up logs

* add userID module handling

* update md && clean up

* Send setupad only on bidRequested

* Fix bidResponse and bidWon responses

* Improve bidResponse and bidWon logic

* Revert changes to specific files

* Remove test parameter

* Fix multiple bidResponse and bidTimeout calls to getPixelUrl

* eslint errors fixes(brackets added)

* Add extra checks for events

* Fix BIDDER_CODE const

* update reporting endpoint

* update setupadBidAdapter_spec.js REPORT_ENDPOINT

* update readme

* Revert "Merge branch 'prebid:master' into setupad-adapter"

This reverts commit 1c14dbe, reversing
changes made to 7fe9ea5.

* Revert "Revert "Merge branch 'prebid:master' into setupad-adapter""

This reverts commit a34e3e4.

* # This is a combination of 20 commits.
# This is the 1st commit message:

add setupadBidAdapter

# This is the commit message #2:

update setupadBidAdapter

# This is the commit message #3:

update metrics collection

# This is the commit message #4:

update analytics collection

# This is the commit message #5:

update getUserSyncs

# This is the commit message #6:

add setupadAnalyticsAdapter.js

# This is the commit message #7:

test setupadAnalyticsAdapter

# This is the commit message #8:

remove test: 1

# This is the commit message #9:

add GVLID && bug fixes && test updates

# This is the commit message #10:

remove setupadAnalyticsAdapter

# This is the commit message #11:

add userID module handling

# This is the commit message #12:

clean up && seat bugfix

# This is the commit message #13:

add userID module handling

# This is the commit message #14:

add GVLID && bug fixes && test updates

# This is the commit message #15:

remove setupadAnalyticsAdapter

# This is the commit message #16:

add userID module handling

# This is the commit message #17:

clean up logs

# This is the commit message #18:

update md && clean up

# This is the commit message #19:

Send setupad only on bidRequested

# This is the commit message #20:

Fix bidResponse and bidWon responses

* # This is a combination of 22 commits.tree 8abae7e6dffc9a21ad11770713ba485fc610028a
parent cecfce3
author pavel <pavel@setupad.com> 1706627437 +0200
committer pavel <pavel@setupad.com> 1706627437 +0200
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEqGYI3KX/FkbObQG8FABtd4pCs/AFAmW5EW0ACgkQFABtd4pC
 s/CK3w//WWJSFUlycnnNKTV2XfdcBjooOeZZvjpXVthwr09CCC4uO//kw4bPluhn
 f5fcVFdXzrY1AZ6ch8Wo3msX/Pkso014jIGd5aIWcHpNYFtffACwH/40Y8AcJNZd
 bsOZxVK0awPTz/RihC5eY+0J3cP+iFWP/FlYJoHEQIBXq/Eg6mWoAhxwpL/JvxbY
 QbLFWsRn2ckQ6ftOZgm3/jh8VLaG1zWbWImlWEs5Zel+CorJBTniTj58VbApelYD
 TFMgbSR2I4NGVaqNIrHePnSMsDATxalQ2nZPwY6raKCHWIbvoUPIn/OpDMMbKgC7
 nCwounNmObxFVoj3xusAZppzHpKPasY8xKWb2Kr7zfhZArsOMC6B7fYqQNK0cWG3
 8RR/10oheJD9M2kRlfLiqnRv7ExY08SQ/ZMo9LA8BeRUGBXhh6++8FKhKIHvX1gL
 k1R5W6c+NNWP+PDFsmrFpMn+LpYdl84I7yfYK5dHuw80od7f1wuAVYpswi6Cziy9
 /KY6/rfENvUrGTmWSh5GdDBel89ACCfFkasIKB92xhzKTfjzF/DXkc8XQZOMbt1j
 CsILgWMNfLPMo4Dlgdx/tYCSLLBNEtZ1/hhUcFQ3+0TzLf0GtMkvMnlBnDinqe1n
 1P30fQ2I5W5NJKDPrCOnRymI6QOAPFXtMF11R81mbB9H8asft/E=
 =oJtZ
 -----END PGP SIGNATURE-----

bugfixes

# This is the commit message #22:

Remove test parameter

* # This is a combination of 26 commits.
parent cecfce3
author pavel <pavel@setupad.com> 1706627437 +0200
committer pavel <pavel@setupad.com> 1706627437 +0200
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEqGYI3KX/FkbObQG8FABtd4pCs/AFAmW5EW0ACgkQFABtd4pC
 s/CK3w//WWJSFUlycnnNKTV2XfdcBjooOeZZvjpXVthwr09CCC4uO//kw4bPluhn
 f5fcVFdXzrY1AZ6ch8Wo3msX/Pkso014jIGd5aIWcHpNYFtffACwH/40Y8AcJNZd
 bsOZxVK0awPTz/RihC5eY+0J3cP+iFWP/FlYJoHEQIBXq/Eg6mWoAhxwpL/JvxbY
 QbLFWsRn2ckQ6ftOZgm3/jh8VLaG1zWbWImlWEs5Zel+CorJBTniTj58VbApelYD
 TFMgbSR2I4NGVaqNIrHePnSMsDATxalQ2nZPwY6raKCHWIbvoUPIn/OpDMMbKgC7
 nCwounNmObxFVoj3xusAZppzHpKPasY8xKWb2Kr7zfhZArsOMC6B7fYqQNK0cWG3
 8RR/10oheJD9M2kRlfLiqnRv7ExY08SQ/ZMo9LA8BeRUGBXhh6++8FKhKIHvX1gL
 k1R5W6c+NNWP+PDFsmrFpMn+LpYdl84I7yfYK5dHuw80od7f1wuAVYpswi6Cziy9
 /KY6/rfENvUrGTmWSh5GdDBel89ACCfFkasIKB92xhzKTfjzF/DXkc8XQZOMbt1j
 CsILgWMNfLPMo4Dlgdx/tYCSLLBNEtZ1/hhUcFQ3+0TzLf0GtMkvMnlBnDinqe1n
 1P30fQ2I5W5NJKDPrCOnRymI6QOAPFXtMF11R81mbB9H8asft/E=
 =oJtZ
 -----END PGP SIGNATURE-----

bugfixes

# This is the commit message #22:

Remove test parameter

# This is the commit message #23:

Fix multiple bidResponse and bidTimeout calls to getPixelUrl

# This is the commit message #25:

eslint errors fixes(brackets added)

# This is the commit message #26:

Add extra checks for events

* parent 75178b9
author pavel <pavel@setupad.com> 1706627694 +0200
committer pavel <pavel@setupad.com> 1706627694 +0200
gpgsig -----BEGIN PGP SIGNATURE-----

 iQIzBAABCAAdFiEEqGYI3KX/FkbObQG8FABtd4pCs/AFAmW5Em4ACgkQFABtd4pC
 s/BBUQ/+NXyHoxPM185YJLG9M1ySC/5vTT9W5mfwQ93cVDLCeuGnpsnmi4S21NuQ
 b7gSeokFjwztvVOUmh/xqMp4lTsvL53TUd00b1k4KGVSqgcF00Foit5g8fOGLYsI
 DAoqphYV6MWjpAun+II+ELY8QUkHR1cjTc7PEGtmf+8RnptGVdyJ6C9Ab8u9TQTY
 Apj5Srhfo3Tl8S+WScOxwwB/uqEJR4fhIrJyzFzdLDEb2olSPyrQUs87vQXlhEnK
 buPEg2F5JsRH6sw11Xp3TFNSZGxNnBSlTh9dixou5md4yRCv5a2TMef667N0BVDp
 lGgc7mCrRKXyqzphmmeHudiscEGFjtUPObXoHutSVw22wdARFCTpNFKBLLFn4v8o
 Zv1OvFdNprvHsoeW0HVlZdU7OKnDTRrko6DHk2AahxojjvAFEWuDsGYZNjhdQwRR
 lK1zm+SFQnKI0Eojd+f84fvKod9geGs640jyH/x5R4eYm4yjZb8SkRtd3cca88wS
 OuGq9LIkbU428b46l7VnDwudldTXPUU8eKfUtFRjdGtIWH9I3tK6TsRoCfTcXkv0
 smxYiiU1XHjAkkPFWQWEeFdfZ071snFKVWouU0AoKiq+PdRoS8+3AJqIQUjlA2sH
 AybnSkv9KxY/Rs1bnvMubsQm1GF66qVrbxBU6FILBv1JZYwj4yA=
 =Gbog
 -----END PGP SIGNATURE-----

bugfixes

update setupadBidAdapter_spec.js REPORT_ENDPOINT

update readme

Revert "Merge branch 'prebid:master' into setupad-adapter"

This reverts commit 1c14dbe, reversing
changes made to 7fe9ea5.

Revert "Revert "Merge branch 'prebid:master' into setupad-adapter""

This reverts commit a34e3e4.

* change double quote to single quote

---------

Co-authored-by: pavel <pavel@setupad.com>
Co-authored-by: Elgars Grodnis <elgars@setupad.com>

* bugfix setupadBidAdapter

remove getAdEl, spelling correction

* add onBidWon event

onBidWon event handling moved from custom to native onBidWon method

* minor bugfixes && remove funk getSiteObj && getDeviceObj

---------

Co-authored-by: pavel <pavel@setupad.com>
Co-authored-by: Elgars Grodnis <elgars@setupad.com>
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.

6 participants