1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-25 18:56:28 +02:00

Rewrite requests module using js-utility-belt's request

This commit is contained in:
Brett Sun 2016-06-14 16:53:18 +02:00
parent 136213856b
commit 2309e21571
27 changed files with 314 additions and 324 deletions

View File

@ -11,10 +11,10 @@ import AlertDismissable from './alert';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import requests from '../../utils/requests';
import { sanitize } from '../../utils/general';
import { getLangText } from '../../utils/lang';
import request from '../../utils/request';
let Form = React.createClass({
@ -95,32 +95,29 @@ let Form = React.createClass({
}
},
post() {
requests
.post(this.props.url, { body: this.getFormData() })
request(method) {
request(this.props.url, {
method,
jsonBody: this.getFormData()
})
.then(this.handleSuccess)
.catch(this.handleError);
},
post() {
this.request('POST');
},
put() {
requests
.put(this.props.url, { body: this.getFormData() })
.then(this.handleSuccess)
.catch(this.handleError);
this.request('PUT');
},
patch() {
requests
.patch(this.props.url, { body: this.getFormData() })
.then(this.handleSuccess)
.catch(this.handleError);
this.request('PATCH');
},
delete() {
requests
.delete(this.props.url, this.getFormData())
.then(this.handleSuccess)
.catch(this.handleError);
this.request('DELETE');
},
getFormData() {

View File

@ -9,6 +9,7 @@ import AclInformation from '../ascribe_buttons/acl_information';
import AscribeSpinner from '../ascribe_spinner';
import { getLangText } from '../../utils/lang';
import { formatText } from '../../utils/text';
import { resolveUrl } from '../../utils/url_resolver';
@ -22,25 +23,22 @@ let EditionDeleteForm = React.createClass({
},
getBitcoinIds() {
return this.props.editions.map(function(edition){
return edition.bitcoin_id;
return this.props.editions.map((edition) => edition.bitcoin_id);
},
getUrl() {
return formatText(resolveUrl('edition_delete'), {
// Since this form can be used for either deleting a single edition or multiple we need
// to call getBitcoinIds to get the value of edition_id
editionId: this.getBitcoinIds().join(',')
});
},
// Since this form can be used for either deleting a single edition or multiple
// we need to call getBitcoinIds to get the value of edition_id
getFormData() {
return {
edition_id: this.getBitcoinIds().join(',')
};
},
render () {
render() {
return (
<Form
ref='form'
url={resolveUrl('edition_delete')}
getFormData={this.getFormData}
url={this.getUrl()}
method="delete"
handleSuccess={this.props.handleSuccess}
buttons={

View File

@ -9,6 +9,7 @@ import AclInformation from '../ascribe_buttons/acl_information';
import AscribeSpinner from '../ascribe_spinner';
import { getLangText } from '../../utils/lang';
import { formatText } from '../../utils/text';
import { resolveUrl } from '../../utils/url_resolver';
@ -20,18 +21,17 @@ let PieceDeleteForm = React.createClass({
handleSuccess: React.PropTypes.func
},
getFormData() {
return {
piece_id: this.props.pieceId
};
getUrl() {
return formatText(resolveUrl('piece'), {
pieceId: this.props.pieceId
});
},
render() {
return (
<Form
ref='form'
url={resolveUrl('piece')}
getFormData={this.getFormData}
url={this.getUrl()}
method="delete"
handleSuccess={this.props.handleSuccess}
buttons={

View File

@ -6,8 +6,8 @@ import Form from './form';
import Property from './property';
import InputTextAreaToggable from './input_textarea_toggable';
import requests from '../../utils/requests';
import { getLangText } from '../../utils/lang.js';
import { formatText } from '../../utils/text';
import { resolveUrl } from '../../utils/url_resolver';
@ -24,16 +24,24 @@ let PieceExtraDataForm = React.createClass({
},
getFormData() {
const { name, pieceId } = this.props;
return {
extradata: {
[this.props.name]: this.refs.form.refs[this.props.name].state.value
[name]: this.refs.form.refs[name].state.value
},
piece_id: this.props.pieceId
piece_id: pieceId
};
},
getUrl() {
return formatText(resolveUrl('piece_extradata'), {
pieceId: this.props.pieceId
});
},
render() {
const { convertLinks, editable, extraData, handleSuccess, name, pieceId, title } = this.props;
const { convertLinks, editable, extraData, handleSuccess, name, title } = this.props;
const defaultValue = (extraData && extraData[name]) || null;
if (!defaultValue && !editable) {
@ -46,7 +54,7 @@ let PieceExtraDataForm = React.createClass({
disabled={!editable}
getFormData={this.getFormData}
handleSuccess={handleSuccess}
url={requests.prepareUrl(resolveUrl('piece_extradata'), { piece_id: pieceId })}>
url={this.getUrl()}>
<Property
name={name}
label={title}>

View File

@ -7,6 +7,7 @@ import Form from './form';
import AscribeSpinner from '../ascribe_spinner';
import { getLangText } from '../../utils/lang';
import { formatText } from '../../utils/text';
import { resolveUrl } from '../../utils/url_resolver';
let EditionRemoveFromCollectionForm = React.createClass({
@ -18,25 +19,22 @@ let EditionRemoveFromCollectionForm = React.createClass({
},
getBitcoinIds() {
return this.props.editions.map(function(edition){
return edition.bitcoin_id;
});
return this.props.editions.map((edition) => edition.bitcoin_id);
},
// Since this form can be used for either removing a single edition or multiple
// we need to call getBitcoinIds to get the value of edition_id
getFormData() {
return {
edition_id: this.getBitcoinIds().join(',')
};
getUrl() {
return formatText(resolveUrl('edition_remove_from_collection'), {
// Since this form can be used for either deleting a single edition or multiple we need
// to call getBitcoinIds to get the value of edition_id
editionId: this.getBitcoinIds().join(',')
});
},
render() {
return (
<Form
ref='form'
url={resolveUrl('edition_remove_from_collection')}
getFormData={this.getFormData}
url={this.getUrl()}
method="delete"
handleSuccess={this.props.handleSuccess}
buttons={

View File

@ -18,18 +18,17 @@ let PieceRemoveFromCollectionForm = React.createClass({
handleSuccess: React.PropTypes.func
},
getFormData() {
return {
piece_id: this.props.pieceId
};
getUrl() {
return formatText(resolveUrl('piece_remove_from_collection'), {
pieceId: this.props.pieceId
});
},
render () {
return (
<Form
ref='form'
url={resolveUrl('piece_remove_from_collection')}
getFormData={this.getFormData}
url={this.getUrl()}
method="delete"
handleSuccess={this.props.handleSuccess}
buttons={

View File

@ -16,7 +16,7 @@ import AscribeSpinner from '../../../../../ascribe_spinner';
import { getLangText } from '../../../../../../utils/lang';
import { formSubmissionValidation } from '../../../../../ascribe_uploader/react_s3_fine_uploader_utils';
import requests from '../../../../../../utils/requests';
import { formatText } from '../../../../../../utils/text';
import { resolveUrl } from '../../../../../../utils/url_resolver';
@ -58,10 +58,15 @@ let CylandAdditionalDataForm = React.createClass({
});
return {
extradata: extradata,
extradata,
piece_id: this.props.piece.id
};
},
getUrl() {
return formatText(resolveUrl('piece_extradata'), {
pieceId: this.props.piece.id
});
},
setIsUploadReady(isReady) {
@ -112,7 +117,7 @@ let CylandAdditionalDataForm = React.createClass({
disabled={disabled}
className="ascribe-form-bordered"
ref='form'
url={requests.prepareUrl(resolveUrl('piece_extradata'), { piece_id: piece.id })}
url={this.getUrl()}
handleSuccess={handleSuccess || this.handleSuccess}
getFormData={this.getFormData}
buttons={buttons}

View File

@ -13,7 +13,7 @@ import GlobalNotificationActions from '../../../../../../actions/global_notifica
import AscribeSpinner from '../../../../../ascribe_spinner';
import { getLangText } from '../../../../../../utils/lang';
import requests from '../../../../../../utils/requests';
import { formatText } from '../../../../../../utils/text';
import { resolveUrl } from '../../../../../../utils/url_resolver';
@ -44,10 +44,15 @@ let IkonotvArtistDetailsForm = React.createClass({
});
return {
extradata: extradata,
extradata,
piece_id: this.props.piece.id
};
},
getUrl() {
return formatText(resolveUrl('piece_extradata'), {
pieceId: this.props.piece.id
});
},
handleSuccess() {
@ -97,7 +102,7 @@ let IkonotvArtistDetailsForm = React.createClass({
disabled={disabled}
className="ascribe-form-bordered"
ref='form'
url={requests.prepareUrl(resolveUrl('piece_extradata'), { piece_id: piece.id })}
url={this.getUrl()}
handleSuccess={handleSuccess || this.handleSuccess}
getFormData={this.getFormData}
buttons={buttons}

View File

@ -13,7 +13,7 @@ import GlobalNotificationActions from '../../../../../../actions/global_notifica
import AscribeSpinner from '../../../../../ascribe_spinner';
import { getLangText } from '../../../../../../utils/lang';
import requests from '../../../../../../utils/requests';
import { formatText } from '../../../../../../utils/text';
import { resolveUrl } from '../../../../../../utils/url_resolver';
@ -44,10 +44,15 @@ let IkonotvArtworkDetailsForm = React.createClass({
});
return {
extradata: extradata,
extradata,
piece_id: this.props.piece.id
};
},
getUrl() {
return formatText(resolveUrl('piece_extradata'), {
pieceId: this.props.piece.id
});
},
handleSuccess() {
@ -97,7 +102,7 @@ let IkonotvArtworkDetailsForm = React.createClass({
disabled={disabled}
className="ascribe-form-bordered"
ref='form'
url={requests.prepareUrl(resolveUrl('piece_extradata'), { piece_id: piece.id })}
url={this.getUrl()}
handleSuccess={handleSuccess || this.handleSuccess}
getFormData={this.getFormData}
buttons={buttons}

View File

@ -17,7 +17,7 @@ import { validationParts, validationTypes } from '../../../../../../constants/up
import { formSubmissionValidation } from '../../../../../ascribe_uploader/react_s3_fine_uploader_utils';
import { getLangText } from '../../../../../../utils/lang';
import requests from '../../../../../../utils/requests';
import { formatText } from '../../../../../../utils/text';
import { resolveUrl } from '../../../../../../utils/url_resolver';
@ -80,11 +80,17 @@ let MarketAdditionalDataForm = React.createClass({
});
return {
extradata: extradata,
extradata,
piece_id: this.props.pieceId
};
},
getUrl() {
return formatText(resolveUrl('piece_extradata'), {
pieceId: this.props.pieceId
});
},
isUploadReadyOnChange() {
return this.props.otherData && this.props.otherData.length;
},
@ -152,7 +158,7 @@ let MarketAdditionalDataForm = React.createClass({
className="ascribe-form-bordered"
ref='form'
key={this.state.forceUpdateKey}
url={requests.prepareUrl(resolveUrl('piece_extradata'), { piece_id: pieceId })}
url={this.getUrl()}
handleSuccess={showNotification ? this.handleSuccessWithNotification : handleSuccess}
getFormData={this.getFormData}
buttons={buttons}

View File

@ -7,25 +7,25 @@ function getWalletApiUrls(subdomain) {
if (subdomain === 'cyland') {
return {
'pieces_list': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/${piece_id}/',
'piece_extradata': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/${piece_id}/extradata/',
'piece': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/${pieceId}/',
'piece_extradata': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/${pieceId}/extradata/',
'user': walletConstants.walletApiEndpoint + '/' + subdomain + '/users/'
};
} else if (subdomain === 'ikonotv') {
return {
'pieces_list': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/${piece_id}/',
'piece': walletConstants.walletApiEndpoint + '/' + subdomain + '/pieces/${pieceId}/',
'user': walletConstants.walletApiEndpoint + '/' + subdomain + '/users/'
};
} else if (subdomain === 'lumenus' || subdomain === '23vivi' ||
subdomain === 'polline' || subdomain === 'artcity' ||
subdomain === 'demo' || subdomain === 'liquidgallery') {
return {
'editions_list': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/${piece_id}/editions/',
'edition': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/editions/${bitcoin_id}/',
'editions_list': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/${pieceId}/editions/',
'edition': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/editions/${bitcoinId}/',
'pieces_list': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/',
'piece': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/${piece_id}/',
'piece_extradata': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/${piece_id}/extradata/',
'piece': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/${pieceId}/',
'piece_extradata': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/pieces/${pieceId}/extradata/',
'user': walletConstants.walletApiEndpoint + '/markets/' + subdomain + '/users/'
};
}

View File

@ -11,24 +11,24 @@ const ApiUrls = {
'coa': AppConstants.apiEndpoint + '/coa/${id}/',
'coa_create': AppConstants.apiEndpoint + '/coa/',
'coa_verify': AppConstants.apiEndpoint + '/coa/verify_coa/',
'edition': AppConstants.apiEndpoint + '/editions/${bitcoin_id}/',
'edition_delete': AppConstants.apiEndpoint + '/editions/${edition_id}/',
'edition_remove_from_collection': AppConstants.apiEndpoint + '/ownership/shares/editions/${edition_id}/',
'edition': AppConstants.apiEndpoint + '/editions/${bitcoinId}/',
'edition_delete': AppConstants.apiEndpoint + '/editions/${editionId}/',
'edition_remove_from_collection': AppConstants.apiEndpoint + '/ownership/shares/editions/${editionId}/',
'editions': AppConstants.apiEndpoint + '/editions/', // this should be moved to the one below
'editions_list': AppConstants.apiEndpoint + '/pieces/${piece_id}/editions/',
'editions_list': AppConstants.apiEndpoint + '/pieces/${pieceId}/editions/',
'licenses': AppConstants.apiEndpoint + '/ownership/licenses/',
'note_private_edition': AppConstants.apiEndpoint + '/note/private/editions/',
'note_private_piece': AppConstants.apiEndpoint + '/note/private/pieces/',
'note_public_edition': AppConstants.apiEndpoint + '/note/public/editions/',
'note_public_piece': AppConstants.apiEndpoint + '/note/public/pieces/',
'notification_piecelist': AppConstants.apiEndpoint + '/notifications/pieces/',
'notification_piece': AppConstants.apiEndpoint + '/notifications/pieces/${piece_id}/',
'notification_piece': AppConstants.apiEndpoint + '/notifications/pieces/${pieceId}/',
'notification_editionlist': AppConstants.apiEndpoint + '/notifications/editions/',
'notification_edition': AppConstants.apiEndpoint + '/notifications/editions/${edition_id}/',
'notification_contractagreementlist': AppConstants.apiEndpoint + '/notifications/contract_agreements/',
'notification_edition': AppConstants.apiEndpoint + '/notifications/editions/${editionId}/',
'notification_contract_agreement_list': AppConstants.apiEndpoint + '/notifications/contract_agreements/',
'ownership_contract_agreements': AppConstants.apiEndpoint + '/ownership/contract_agreements/',
'ownership_contract_agreements_confirm': AppConstants.apiEndpoint + '/ownership/contract_agreements/${contract_agreement_id}/accept/',
'ownership_contract_agreements_deny': AppConstants.apiEndpoint + '/ownership/contract_agreements/${contract_agreement_id}/reject/',
'ownership_contract_agreements_confirm': AppConstants.apiEndpoint + '/ownership/contract_agreements/${contractAgreementId}/accept/',
'ownership_contract_agreements_deny': AppConstants.apiEndpoint + '/ownership/contract_agreements/${contractAgreementId}/reject/',
'ownership_consigns': AppConstants.apiEndpoint + '/ownership/consigns/',
'ownership_consigns_confirm': AppConstants.apiEndpoint + '/ownership/consigns/confirm/',
'ownership_consigns_deny': AppConstants.apiEndpoint + '/ownership/consigns/deny/',
@ -49,12 +49,12 @@ const ApiUrls = {
'ownership_unconsigns': AppConstants.apiEndpoint + '/ownership/unconsigns/',
'ownership_unconsigns_deny': AppConstants.apiEndpoint + '/ownership/unconsigns/deny/',
'ownership_unconsigns_request': AppConstants.apiEndpoint + '/ownership/unconsigns/request/',
'ownership_contract': AppConstants.apiEndpoint + '/ownership/contracts/${contract_id}/',
'ownership_contract': AppConstants.apiEndpoint + '/ownership/contracts/${contractId}/',
'ownership_contract_list': AppConstants.apiEndpoint + '/ownership/contracts/',
'piece': AppConstants.apiEndpoint + '/pieces/${piece_id}/',
'piece_extradata': AppConstants.apiEndpoint + '/pieces/${piece_id}/extradata/',
'piece': AppConstants.apiEndpoint + '/pieces/${pieceId}/',
'piece_extradata': AppConstants.apiEndpoint + '/pieces/${pieceId}/extradata/',
'pieces_list': AppConstants.apiEndpoint + '/pieces/',
'piece_remove_from_collection': AppConstants.apiEndpoint + '/ownership/shares/pieces/${piece_id}/',
'piece_remove_from_collection': AppConstants.apiEndpoint + '/ownership/shares/pieces/${pieceId}/',
'user': AppConstants.apiEndpoint + '/users/',
'users_login': AppConstants.apiEndpoint + '/users/login/',
'users_logout': AppConstants.apiEndpoint + '/users/logout/',
@ -64,7 +64,7 @@ const ApiUrls = {
'users_username': AppConstants.apiEndpoint + '/users/username/',
'users_profile': AppConstants.apiEndpoint + '/users/profile/',
'wallet_settings': AppConstants.apiEndpoint + '/users/wallet_settings/',
'webhook': AppConstants.apiEndpoint + '/webhooks/${webhook_id}/',
'webhook': AppConstants.apiEndpoint + '/webhooks/${webhookId}/',
'webhooks': AppConstants.apiEndpoint + '/webhooks/',
'webhooks_events': AppConstants.apiEndpoint + '/webhooks/events/',
'whitelabel_settings': AppConstants.apiEndpoint + '/whitelabel/settings/${subdomain}/',

View File

@ -1,16 +1,20 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
let ApplicationFetcher = {
/**
* Fetch the registered applications of a user from the API.
*/
fetch() {
return requests.get('applications');
return request('applications');
},
refreshToken(applicationName) {
return requests.post('application_token_refresh', { body: {'name': applicationName}});
return request('application_token_refresh', {
method: 'POST',
jsonBody: { name: applicationName }
});
}
};

View File

@ -1,6 +1,6 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
import { safeMerge } from '../utils/general';
import { generateOrderingQueryParams } from '../utils/url';
@ -11,18 +11,19 @@ let EditionListFetcher = {
*/
fetch({ pieceId, page, pageSize, orderBy, orderAsc, filterBy }) {
const ordering = generateOrderingQueryParams(orderBy, orderAsc);
const queryParams = safeMerge(
const query = safeMerge(
{
page,
pageSize,
ordering,
piece_id: pieceId
ordering
},
filterBy
);
return requests.get('editions_list', queryParams);
return request('editions_list', {
query,
urlTemplateSpec: { pieceId }
});
}
};

View File

@ -1,6 +1,6 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
import { getCurrentSubdomain } from '../utils/url';
@ -10,7 +10,11 @@ let LicenseFetcher = {
* Fetch the available licenses from the API (might be bound to the subdomain e.g. cc.ascribe.io).
*/
fetch() {
return requests.get('licenses', { 'subdomain': getCurrentSubdomain() });
const query = {
subdomain: getCurrentSubdomain()
};
return request('licenses', { query });
}
};

View File

@ -1,28 +1,31 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
let NotificationFetcher = {
fetchPieceListNotifications() {
return requests.get('notification_piecelist');
return request('notification_piecelist');
},
fetchPieceNotifications(pieceId) {
return requests.get('notification_piece', {'piece_id': pieceId});
return request('notification_piece', {
urlTemplateSpec: { pieceId }
});
},
fetchEditionListNotifications() {
return requests.get('notification_editionlist');
return request('notification_editionlist');
},
fetchEditionNotifications(editionId) {
return requests.get('notification_edition', {'edition_id': editionId});
return request('notification_edition', {
urlTemplateSpec: { editionId }
});
},
fetchContractAgreementListNotifications() {
return requests.get('notification_contractagreementlist');
return request('notification_contract_agreement_list');
}
};

View File

@ -1,27 +1,25 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
// FIXME: fix query string usage
let OwnershipFetcher = {
/**
* Fetch the default, public contract of a user from the API.
*/
fetchContract(loanee) {
return requests.get('blob_contracts', { loanee });
return request('blob_contracts', {
query: { loanee }
});
},
/**
* Fetch the contracts of the logged-in user from the API.
*/
fetchContractList(isActive, isPublic, issuer) {
let queryParams = {
isActive,
isPublic,
issuer
};
return requests.get('ownership_contract_list', queryParams);
const query = { isActive, isPublic, issuer };
return request('ownership_contract_list', { query });
},
@ -29,39 +27,61 @@ let OwnershipFetcher = {
* Create a contractagreement between the logged-in user and the email from the API with contract.
*/
createContractAgreement(signee, contractObj) {
return requests.post('ownership_contract_agreements', { body: {signee: signee, contract: contractObj.id }});
return request('ownership_contract_agreements', {
method: 'POST',
jsonBody: {
signee,
contract: contractObj.id
}
});
},
/**
* Fetch the contractagreement between the logged-in user and the email from the API.
*/
fetchContractAgreementList(issuer, accepted, pending) {
let queryParams = {
issuer,
accepted,
pending
};
return requests.get('ownership_contract_agreements', queryParams);
const query = { issuer, accepted, pending };
return request('ownership_contract_agreements', { query });
},
confirmContractAgreement(contractAgreement) {
return requests.put('ownership_contract_agreements_confirm', {contract_agreement_id: contractAgreement.id});
return request('ownership_contract_agreements_confirm', {
method: 'PUT',
urlTemplateSpec: {
contractAgreementId: contractAgreement.id
}
});
},
denyContractAgreement(contractAgreement) {
return requests.put('ownership_contract_agreements_deny', {contract_agreement_id: contractAgreement.id});
return request('ownership_contract_agreements_deny', {
method: 'PUT',
urlTemplateSpec: {
contractAgreementId: contractAgreement.id
}
});
},
fetchLoanPieceRequestList() {
return requests.get('ownership_loans_pieces_request');
return request('ownership_loans_pieces_request');
},
changeContract(contractObj) {
return requests.put('ownership_contract', { body: contractObj, contract_id: contractObj.id });
return request('ownership_contract', {
method: 'PUT',
jsonBody: contractObj,
urlTemplateSpec: {
contractId: contractObj.id
}
});
},
deleteContract(contractObjId) {
return requests.delete('ownership_contract', {contract_id: contractObjId});
deleteContract(contractId) {
return request('ownership_contract', {
method: 'DELETE',
urlTemplateSpec: { contractId }
});
}
};

View File

@ -1,6 +1,6 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
import { safeMerge } from '../utils/general';
import { generateOrderingQueryParams } from '../utils/url';
@ -15,7 +15,7 @@ let PieceListFetcher = {
// filterBy is an object of acl key-value pairs.
// The values are booleans
const queryParams = safeMerge(
const query = safeMerge(
{
page,
pageSize,
@ -25,11 +25,11 @@ let PieceListFetcher = {
filterBy
);
return requests.get('pieces_list', queryParams);
return request('pieces_list', { query });
},
fetchRequestActions() {
return requests.get('pieces_list_request_actions');
return request('pieces_list_request_actions');
}
};

View File

@ -1,22 +1,21 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
let S3Fetcher = {
/**
* Fetch the registered applications of a user from the API.
*/
deleteFile(key, bucket) {
return requests.delete('delete_s3_file', {
key,
bucket
return request('delete_s3_file', {
method: 'DELETE',
query: { bucket, key }
});
},
signUrl(key, title, artistName) {
return requests.get('sign_url_s3', {
key,
title,
'artist_name': artistName
return request('sign_url_s3', {
query: { artistName, key, title }
});
}
};

View File

@ -1,11 +1,11 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
let WalletSettingsFetcher = {
fetchOne() {
return requests.get('wallet_settings');
return request('wallet_settings');
}
};

View File

@ -1,6 +1,6 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
import EditionActions from '../actions/edition_actions';
@ -8,12 +8,14 @@ import EditionActions from '../actions/edition_actions';
const CoaSource = {
lookupCoa: {
remote(state, coaId) {
return requests
.get('coa', { id: coaId })
.then((res) => {
// If no coa is found here, fake a 404 error so the error action can pick it up
return (res && res.coa) ? res : Promise.reject({ json: { status: 404 } });
});
return request('coa', {
urlTemplateSpec: {
id: coaId
}
}).then((res) => (
// If no coa is found here, fake a 404 error so the error action can pick it up
(res && res.coa) ? res : Promise.reject({ json: { status: 404 } })
));
},
success: EditionActions.successFetchCoa,
@ -22,7 +24,10 @@ const CoaSource = {
performCreateCoaForEdition: {
remote(state, editionId) {
return requests.post('coa_create', { body: { bitcoin_id: editionId } });
return request('coa_create', {
method: 'POST',
jsonBody: { bitcoin_id: editionId }
});
},
success: EditionActions.successFetchCoa,

View File

@ -1,14 +1,16 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
import EditionActions from '../actions/edition_actions';
const EditionSource = {
lookupEdition: {
remote(state, editionId) {
return requests.get('edition', { bitcoin_id: editionId });
remote(state, bitcoinId) {
return request('edition', {
urlTemplateSpec: { bitcoinId }
});
},
success: EditionActions.successFetchEdition,

View File

@ -1,6 +1,6 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
import PieceActions from '../actions/piece_actions';
@ -8,7 +8,9 @@ import PieceActions from '../actions/piece_actions';
const PieceSource = {
lookupPiece: {
remote(state, pieceId) {
return requests.get('piece', { piece_id: pieceId });
return request('piece', {
urlTemplateSpec: { pieceId }
});
},
success: PieceActions.successFetchPiece,

View File

@ -2,13 +2,13 @@
import UserActions from '../actions/user_actions';
import requests from '../utils/requests';
import request from '../utils/request';
const UserSource = {
lookupCurrentUser: {
remote() {
return requests.get('user');
return request('user');
},
local(state) {
@ -25,7 +25,7 @@ const UserSource = {
performLogoutCurrentUser: {
remote() {
return requests.get('users_logout');
return request('users_logout');
},
success: UserActions.successLogoutCurrentUser,

View File

@ -1,6 +1,6 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
import WebhookActions from '../actions/webhook_actions';
@ -8,7 +8,7 @@ import WebhookActions from '../actions/webhook_actions';
const WebhookSource = {
lookupWebhooks: {
remote() {
return requests.get('webhooks');
return request('webhooks');
},
local(state) {
@ -25,7 +25,7 @@ const WebhookSource = {
lookupWebhookEvents: {
remote() {
return requests.get('webhooks_events');
return request('webhooks_events');
},
local(state) {
@ -42,7 +42,10 @@ const WebhookSource = {
performRemoveWebhook: {
remote(state, webhookId) {
return requests.delete('webhook', { 'webhook_id': webhookId });
return request('webhook', {
method: 'DELETE',
urlTemplateSpec: { webhookId }
});
},
success: WebhookActions.successRemoveWebhook,

View File

@ -1,6 +1,6 @@
'use strict';
import requests from '../utils/requests';
import request from '../utils/request';
import WhitelabelActions from '../actions/whitelabel_actions';
import { getCurrentSubdomain } from '../utils/url';
@ -9,7 +9,11 @@ import { getCurrentSubdomain } from '../utils/url';
const WhitelabelSource = {
lookupWhitelabel: {
remote() {
return requests.get('whitelabel_settings', { 'subdomain': getCurrentSubdomain() });
return request('whitelabel_settings', {
urlTemplateSpec: {
subdomain: getCurrentSubdomain()
}
});
},
local(state) {

View File

@ -1,172 +1,94 @@
'use strict';
import { request as baseRequest, sanitize } from 'js-utility-belt/es6';
import Q from 'q';
import AppConstants from '../constants/application_constants';
import { getCookie } from '../utils/fetch_api';
import { omitFromObject } from '../utils/general';
import { stringifyAsQueryParam } from '../utils/url';
import { makeCsrfHeader } from './csrf';
import { resolveUrl } from './url_resolver';
class Requests {
unpackResponse(url) {
return (response) => {
if (response == null) {
throw new Error(`For: ${url} - Server did not respond to the request. (Not even displayed a 500)`);
}
const DEFAULT_REQUEST_CONFIG = {
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
if (response.status >= 500) {
let err = new Error(`${response.status} - ${response.statusText} - on URL: ${response.url}`);
/**
* Small wrapper around js-utility-belt's request that provides default settings, url mapping, and
* response handling.
*/
export default function request(url, config) {
// Load default fetch configuration and remove any falsy query parameters
const requestConfig = Object.assign({}, DEFAULT_REQUEST_CONFIG, config, config && {
query: config.query && sanitize(config.query)
});
return response
.text()
.then((resText) => {
const resJson = JSON.parse(resText);
err = new Error(resJson.errors.pop());
// Add CSRF token
Object.assign(requestConfig.headers, makeCsrfHeader());
// ES6 promises don't have a .finally() clause so we fake that here by
// forcing the .catch() clause to run
return Promise.reject();
})
// If parsing the resText throws, just rethrow the original error we created
.catch(() => { throw err; });
}
// Resolve url and send request
return new Promise((resolve) => {
resolve(resolveUrl(url));
})
.then((apiUrl) => (
baseRequest(apiUrl, requestConfig)
// Catch any errors resulting from baseRequest first
.catch((res) => {
if (res == null) {
throw new Error(`For: ${apiUrl} - Server did not respond to the request. ` +
'(Not even displayed a 500)');
} else {
let err = new Error(`${res.status} - ${res.statusText} - on URL: ${res.url}`);
return Q.Promise((resolve, reject) => {
response.text()
.then((responseText) => {
// If the responses' body does not contain any data,
// fetch will resolve responseText to the string 'None'.
// If this is the case, we can not try to parse it as JSON.
if (responseText && responseText !== 'None') {
const body = JSON.parse(responseText);
// Try to parse the response body to see if we added more descriptive errors
// before rejecting with the error above.
return res
.json()
.then((body) => {
if (body && Array.isArray(body.errors) && body.errors.length) {
err = new Error(body.errors.pop());
}
if (body && body.errors) {
const error = new Error('Form Error');
// ES6 promises don't have a .finally() clause so we fake that here
// by forcing the .catch() clause to run
return Promise.reject();
})
// If parsing the response body throws, just rethrow the original error
.catch(() => { throw err; });
}
})
// Handle successful requests
.then((res) => res
.json()
.then((body) => {
if (body) {
let error;
if (body.errors) {
error = new Error('Form Error');
error.json = body;
reject(error);
} else if (body && body.detail) {
reject(new Error(body.detail));
} else if (body && 'success' in body && !body.success) {
const error = new Error('Client Request Error');
error.json = {
body: body,
status: response.status,
statusText: response.statusText,
type: response.type,
url: response.url
};
reject(error);
} else {
resolve(body);
} else if (body.detail) {
error = Error(body.detail);
} else if ('success' in body && !body.success) {
const { status, statusText, type, url: resUrl } = res;
error = new Error('Client Request Error');
error.json = { body, status, statusText, type, url: resUrl };
}
if (error) {
throw error;
} else {
return body;
}
} else if (response.status >= 400) {
reject(new Error(`${response.status} - ${response.statusText} - on URL: ${response.url}`));
} else {
resolve({});
return {};
}
}).catch(reject);
});
};
}
getUrl(url) {
// Handle case, that the url string is not defined at all
if (!url) {
throw new Error('Url was not defined and could therefore not be mapped.');
}
let name = url;
if (!url.match(/^http/)) {
url = this.urlMap[url];
if (!url) {
throw new Error(`Cannot find a mapping for "${name}"`);
}
}
return url;
}
prepareUrl(url, params, attachParamsToQuery) {
let newUrl;
let re = /\${(\w+)}/g;
// catch errors and throw them to react
try {
newUrl = this.getUrl(url);
} catch(err) {
})
)
))
// Log any errors and rethrow
.catch((err) => {
console.error(err);
throw err;
}
newUrl = newUrl.replace(re, (match, key) => {
let val = params[key];
if (!val) {
throw new Error(`Cannot find param ${key}`);
}
delete params[key];
return val;
});
if (attachParamsToQuery && params && Object.keys(params).length > 0) {
newUrl += stringifyAsQueryParam(params);
}
return newUrl;
}
request(verb, url, options = {}) {
let merged = Object.assign({}, this.httpOptions, options);
let csrftoken = getCookie(AppConstants.csrftoken);
if (csrftoken) {
merged.headers['X-CSRFToken'] = csrftoken;
}
merged.method = verb;
return fetch(url, merged)
.then(this.unpackResponse(url));
}
get(url, params) {
if (url === undefined) {
throw new Error('Url undefined');
}
let paramsCopy = Object.assign({}, params);
let newUrl = this.prepareUrl(url, paramsCopy, true);
return this.request('get', newUrl);
}
delete(url, params) {
let paramsCopy = Object.assign({}, params);
let newUrl = this.prepareUrl(url, paramsCopy, true);
return this.request('delete', newUrl);
}
_putOrPost(url, paramsAndBody, method) {
let params = omitFromObject(paramsAndBody, ['body']);
let newUrl = this.prepareUrl(url, params);
let body = paramsAndBody && paramsAndBody.body ? JSON.stringify(paramsAndBody.body)
: null;
return this.request(method, newUrl, { body });
}
post(url, params) {
return this._putOrPost(url, params, 'post');
}
put(url, params) {
return this._putOrPost(url, params, 'put');
}
patch(url, params) {
return this._putOrPost(url, params, 'patch');
}
defaults(options) {
this.httpOptions = options.http || {};
this.urlMap = options.urlMap || {};
}
}
let requests = new Requests();
export default requests;