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

Add support for top-level offers end-point with account, selling, and buying filter. #485

Merged
merged 3 commits into from
Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/federation_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,7 @@ export class FederationServer {
} else {
return Promise.reject(
new BadResponseError(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is not related with the PR, but came because of prettier. @tyvdh do you have prettier locally?

Copy link
Contributor

Choose a reason for hiding this comment

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

Like in my ide? I don’t think so.

`Server query failed. Server responded: ${response.status} ${
response.statusText
}`,
`Server query failed. Server responded: ${response.status} ${response.statusText}`,
response.data,
),
);
Expand Down
62 changes: 50 additions & 12 deletions src/offer_call_builder.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,70 @@
import { Asset } from "stellar-base";
import { CallBuilder } from "./call_builder";
import { BadRequestError } from "./errors";
import { ServerApi } from "./server_api";

/**
* Creates a new {@link OfferCallBuilder} pointed to server defined by serverUrl.
* Do not create this object directly, use {@link Server#offers}.
*
* @see [Offers for Account](https://www.stellar.org/developers/horizon/reference/endpoints/offers-for-account.html)
* @see [Offers](https://www.stellar.org/developers/horizon/reference/endpoints/offers.html)
* @class OfferCallBuilder
* @constructor
* @extends CallBuilder
* @param {string} serverUrl Horizon server URL.
* @param {string} resource Resource to query offers
* @param {...string} resourceParams Parameters for selected resource
*/
export class OfferCallBuilder extends CallBuilder<
ServerApi.CollectionPage<ServerApi.OfferRecord>
> {
constructor(
serverUrl: uri.URI,
resource: string,
...resourceParams: string[]
) {
constructor(serverUrl: uri.URI) {
super(serverUrl);
if (resource === "accounts") {
this.url.segment([resource, ...resourceParams, "offers"]);
this.url.segment("offers");
}

/**
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rice2000 can you check the copy on the functions? thanks!

* Returns all offers where the given account is the seller.
*
* @see [Offers](https://www.stellar.org/developers/horizon/reference/endpoints/offers.html)
* @param {string} id For example: `GDGQVOKHW4VEJRU2TETD6DBRKEO5ERCNF353LW5WBFW3JJWQ2BRQ6KDD`
* @returns {OfferCallBuilder} current OfferCallBuilder instance
*/
public forAccount(id: string): this {
this.url.setQuery("seller", id);
return this;
}

/**
* Returns all offers buying an asset.
* @see [Offers](https://www.stellar.org/developers/horizon/reference/endpoints/offers.html)
* @see Asset
* @param {Asset} value For example: `new Asset('USD','GDGQVOKHW4VEJRU2TETD6DBRKEO5ERCNF353LW5WBFW3JJWQ2BRQ6KDD')`
* @returns {OfferCallBuilder} current OfferCallBuilder instance
*/
public buying(asset: Asset): this {
if (!asset.isNative()) {
this.url.setQuery("buying_asset_type", asset.getAssetType());
this.url.setQuery("buying_asset_code", asset.getCode());
this.url.setQuery("buying_asset_issuer", asset.getIssuer());
} else {
this.url.setQuery("buying_asset_type", "native");
}
return this;
}

/**
* Returns all offers selling an asset.
* @see [Offers](https://www.stellar.org/developers/horizon/reference/endpoints/offers.html)
* @see Asset
* @param {Asset} value For example: `new Asset('EUR','GDGQVOKHW4VEJRU2TETD6DBRKEO5ERCNF353LW5WBFW3JJWQ2BRQ6KDD')`
* @returns {OfferCallBuilder} current OfferCallBuilder instance
*/
public selling(asset: Asset): this {
if (!asset.isNative()) {
this.url.setQuery("selling_asset_type", asset.getAssetType());
this.url.setQuery("selling_asset_code", asset.getCode());
this.url.setQuery("selling_asset_issuer", asset.getIssuer());
} else {
throw new BadRequestError("Bad resource specified for offer:", resource);
this.url.setQuery("selling_asset_type", "native");
}
return this;
}
}
22 changes: 8 additions & 14 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,27 +469,21 @@ export class Server {
}

/**
* People on the Stellar network can make offers to buy or sell assets. This endpoint represents all the offers a particular account makes.
* Currently this method only supports querying offers for account and should be used like this:
* People on the Stellar network can make offers to buy or sell assets. This endpoint represents all the offers on the DEX.
*
* You can query all offers for account using the function `.accountId`:
*
* ```
* server.offers('accounts', accountId).call()
* server.offers()
* .forAccount(accountId).call()
* .then(function(offers) {
* console.log(offers);
* });
* ```
* @param {string} resource Resource to query offers
* @param {...string} resourceParams Parameters for selected resource
* @returns {OfferCallBuilder} New {@link OfferCallBuilder} object
*/
public offers(
resource: string,
...resourceParams: string[]
): OfferCallBuilder {
return new OfferCallBuilder(
URI(this.serverURL as any),
resource,
...resourceParams,
);
public offers(): OfferCallBuilder {
return new OfferCallBuilder(URI(this.serverURL as any));
}

/**
Expand Down
139 changes: 110 additions & 29 deletions test/unit/server_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1112,41 +1112,65 @@ describe('server.js non-transaction tests', function() {
});

describe('OfferCallBuilder', function() {
let offersResponse = {
_embedded: {
records: []
},
_links: {
next: {
href:
'/accounts/GBCR5OVQ54S2EKHLBZMK6VYMTXZHXN3T45Y6PRX4PX4FXDMJJGY4FD42/offers?order=asc\u0026limit=10\u0026cursor='
},
prev: {
href:
'/accounts/GBCR5OVQ54S2EKHLBZMK6VYMTXZHXN3T45Y6PRX4PX4FXDMJJGY4FD42/offers?order=desc\u0026limit=10\u0026cursor='
},
self: {
href:
'/accounts/GBCR5OVQ54S2EKHLBZMK6VYMTXZHXN3T45Y6PRX4PX4FXDMJJGY4FD42/offers?order=asc\u0026limit=10\u0026cursor='
}
}
};
const offersResponse = {
_embedded: {
records: []
},
_links: {
next: {
href:
'/offers'
},
prev: {
href:
'/offers'
},
self: {
href:
'/offers'
}
}
};

it('requests the correct endpoint', function(done) {
it('without params requests the correct endpoint', function(done) {
this.axiosMock
.expects('get')
.withArgs(
sinon.match(
'https://horizon-live.stellar.org:1337/accounts/GBS43BF24ENNS3KPACUZVKK2VYPOZVBQO2CISGZ777RYGOPYC2FT6S3K/offers?order=asc'
'https://horizon-live.stellar.org:1337/offers?order=asc'
)
)
.returns(Promise.resolve({ data: offersResponse }));

this.server
.offers(
'accounts',
'GBS43BF24ENNS3KPACUZVKK2VYPOZVBQO2CISGZ777RYGOPYC2FT6S3K'
.offers()
.order('asc')
.call()
.then(function(response) {
expect(response.records).to.be.deep.equal(
offersResponse._embedded.records
);
expect(response.next).to.be.function;
expect(response.prev).to.be.function;
done();
})
.catch(function(err) {
done(err);
});
});

it('forAccount requests the correct endpoint', function(done) {
this.axiosMock
.expects('get')
.withArgs(
sinon.match(
'https://horizon-live.stellar.org:1337/offers?seller=GBS43BF24ENNS3KPACUZVKK2VYPOZVBQO2CISGZ777RYGOPYC2FT6S3K&order=asc'
)
)
.returns(Promise.resolve({ data: offersResponse }));

this.server
.offers()
.forAccount('GBS43BF24ENNS3KPACUZVKK2VYPOZVBQO2CISGZ777RYGOPYC2FT6S3K')
.order('asc')
.call()
.then(function(response) {
Expand All @@ -1161,12 +1185,69 @@ describe('server.js non-transaction tests', function() {
done(err);
});
});
it('selling requests the correct endpoint', function(done) {
const selling = new StellarSdk.Asset(
'USD',
'GDVDKQFP665JAO7A2LSHNLQIUNYNAAIGJ6FYJVMG4DT3YJQQJSRBLQDG'
);

this.axiosMock
.expects('get')
.withArgs(
sinon.match(
'https://horizon-live.stellar.org:1337/offers?selling_asset_type=credit_alphanum4&selling_asset_code=USD&selling_asset_issuer=GDVDKQFP665JAO7A2LSHNLQIUNYNAAIGJ6FYJVMG4DT3YJQQJSRBLQDG&order=asc'
)
)
.returns(Promise.resolve({ data: offersResponse }));

it('rejects the wrong resource', function(done) {
expect(() => this.server.offers('ledgers', '123').call()).to.throw(
/Bad resource specified/
this.server
.offers()
.selling(selling)
.order('asc')
.call()
.then(function(response) {
expect(response.records).to.be.deep.equal(
offersResponse._embedded.records
);
expect(response.next).to.be.function;
expect(response.prev).to.be.function;
done();
})
.catch(function(err) {
done(err);
});
});
it('buying requests the correct endpoint', function(done) {
const buying = new StellarSdk.Asset(
'COP',
'GDVDKQFP665JAO7A2LSHNLQIUNYNAAIGJ6FYJVMG4DT3YJQQJSRBLQDG'
);
done();

this.axiosMock
.expects('get')
.withArgs(
sinon.match(
'https://horizon-live.stellar.org:1337/offers?buying_asset_type=credit_alphanum4&buying_asset_code=COP&buying_asset_issuer=GDVDKQFP665JAO7A2LSHNLQIUNYNAAIGJ6FYJVMG4DT3YJQQJSRBLQDG&order=asc'
)
)
.returns(Promise.resolve({ data: offersResponse }));

this.server
.offers()
.buying(buying)
.order('asc')
.call()
.then(function(response) {
expect(response.records).to.be.deep.equal(
offersResponse._embedded.records
);
expect(response.next).to.be.function;
expect(response.prev).to.be.function;
done();
})
.catch(function(err) {
done(err);
});
});
});

Expand Down