Skip to content

Commit

Permalink
Merge pull request #103 from sharetribe/update-v6.0.0-from-upstream
Browse files Browse the repository at this point in the history
Update v6.0.0 from upstream
  • Loading branch information
OtterleyW authored Jul 1, 2020
2 parents 8ee74f3 + f4c269f commit bdbcbb4
Show file tree
Hide file tree
Showing 53 changed files with 1,725 additions and 190 deletions.
5 changes: 5 additions & 0 deletions .env-template
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ REACT_APP_SHARETRIBE_SDK_CLIENT_ID=change-me
REACT_APP_STRIPE_PUBLISHABLE_KEY=
REACT_APP_MAPBOX_ACCESS_TOKEN=

# If you are using a process with privileged transitions,
# Client Secret needs to be set too. The one related to Client ID.
# You get this at Flex Console (Build -> Applications -> Add new).
SHARETRIBE_SDK_CLIENT_SECRET=

# Or set up an alternative map provider (Google Maps). Check documentation.
# REACT_APP_GOOGLE_MAPS_API_KEY=

Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ https://github.com/sharetribe/flex-template-web/

## Upcoming version 2020-XX-XX

## [v8.0.0] 2020-07-01

### Updates from upstream (FTW-daily v6.0.0)

- [change] Use privileged transitions for price calculation by default and update the process alias.
[#1314](https://github.com/sharetribe/ftw-daily/pull/1314)
- [add] Add client secret enquiry to 'yarn run config' script
[#1313](https://github.com/sharetribe/ftw-daily/pull/1313)
- [change] Add UI support for flexible pricing and privileged transitions. Note that this requires
updating the booking breakdown estimation code that is now done in the backend.
[#1310](https://github.com/sharetribe/ftw-daily/pull/1310)
- [add] Add local API endpoints for flexible pricing and privileged transitions
[#1301](https://github.com/sharetribe/ftw-daily/pull/1301)
- [fix] `yarn run dev-backend` was expecting NODE_ENV.
[#1303](https://github.com/sharetribe/ftw-daily/pull/1303)

[v8.0.0]: https://github.com/sharetribe/flex-template-web/compare/v7.0.0...v8.0.0

## [v7.0.0] 2020-06-04

### Updates from upstream (FTW-daily v5.0.0)
Expand Down
5 changes: 4 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
"description": "See: Integrating to map providers documentation https://github.com/sharetribe/ftw-hourly/blob/master/docs/map-providers.md"
},
"REACT_APP_SHARETRIBE_SDK_CLIENT_ID": {
"description": "Client ID (API key). You will get this from the Sharetribe team."
"description": "Client ID (API key). You will find this from the Flex Console."
},
"SHARETRIBE_SDK_CLIENT_SECRET": {
"description": "Client Secret (API secret token). You will find this from the Flex Console."
},
"REACT_APP_STRIPE_PUBLISHABLE_KEY": {
"description": "Stripe publishable API key for generating tokens with Stripe API. Use test key (prefix pktest) for development."
Expand Down
9 changes: 3 additions & 6 deletions ext/transaction-process/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ This is the transaction process that the Flex Template for Web is designed to wo
The `process.edn` file describes the process flow while the `templates` folder contains notification
messages that are used by the process.

The process uses unit-based booking and time-based pricing i.e. the quantity of booked units should
be calculated from the number of time units. The process has preauthorization and it relies on the
provider to accept booking requests.

For different process descriptions for varying booking and pricing models, see the
[Flex example processes repository](https://github.com/sharetribe/flex-example-processes)
Bookings in the process are time-based. Pricing uses privileged transitions and the
[privileged-set-line-items](https://www.sharetribe.com/docs/references/transaction-process-actions/#actionprivileged-set-line-items)
action. The process has preauthorization and it relies on the provider to accept booking requests.
14 changes: 6 additions & 8 deletions ext/transaction-process/process.edn
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,20 @@
:actions
[{:name :action/create-booking,
:config {:observe-availability? true, :type :time}}
{:name :action/calculate-tx-unit-total-price}
{:name :action/calculate-tx-provider-commission,
:config {:commission 0.1M}}
{:name :action/privileged-set-line-items}
{:name :action/stripe-create-payment-intent}],
:to :state/pending-payment}
:to :state/pending-payment
:privileged? true}
{:name :transition/request-payment-after-enquiry,
:actor :actor.role/customer,
:actions
[{:name :action/create-booking,
:config {:observe-availability? true, :type :time}}
{:name :action/calculate-tx-unit-total-price}
{:name :action/calculate-tx-provider-commission,
:config {:commission 0.1M}}
{:name :action/privileged-set-line-items}
{:name :action/stripe-create-payment-intent}],
:from :state/enquiry,
:to :state/pending-payment}
:to :state/pending-payment
:privileged? true}
{:name :transition/expire-payment,
:at
{:fn/plus
Expand Down
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "app",
"version": "7.0.0",
"version": "8.0.0",
"private": true,
"license": "Apache-2.0",
"dependencies": {
Expand All @@ -17,6 +17,7 @@
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
"core-js": "^3.1.4",
"cors": "^2.8.5",
"decimal.js": "10.2.0",
"dotenv": "6.2.0",
"dotenv-expand": "4.2.0",
Expand Down Expand Up @@ -53,7 +54,7 @@
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"seedrandom": "^3.0.5",
"sharetribe-flex-sdk": "^1.9.1",
"sharetribe-flex-sdk": "1.11.0",
"sharetribe-scripts": "3.1.1",
"smoothscroll-polyfill": "^0.4.0",
"source-map-support": "^0.5.9",
Expand Down Expand Up @@ -89,17 +90,18 @@
"config": "node scripts/config.js",
"config-check": "node scripts/config.js --check",
"dev-frontend": "sharetribe-scripts start",
"dev-backend": "export NODE_ENV=development DEV_API_SERVER_PORT=3500&&nodemon server/apiServer.js",
"dev": "yarn run config-check&&concurrently --kill-others \"yarn run dev-frontend\" \"yarn run dev-backend\"",
"dev-backend": "nodemon server/apiServer.js",
"dev": "yarn run config-check&&export NODE_ENV=development REACT_APP_DEV_API_SERVER_PORT=3500&&concurrently --kill-others \"yarn run dev-frontend\" \"yarn run dev-backend\"",
"build": "sharetribe-scripts build",
"format": "prettier --write '**/*.{js,css}'",
"format-ci": "prettier --list-different '**/*.{js,css}'",
"format-docs": "prettier --write '**/*.md'",
"test": "NODE_ICU_DATA=node_modules/full-icu sharetribe-scripts test",
"test-ci": "sharetribe-scripts test --runInBand",
"test-ci": "yarn run test-server --runInBand && sharetribe-scripts test --runInBand",
"eject": "sharetribe-scripts eject",
"start": "node --icu-data-dir=node_modules/full-icu server/index.js",
"dev-server": "export NODE_ENV=development PORT=4000 REACT_APP_CANONICAL_ROOT_URL=http://localhost:4000&&yarn run build&&nodemon --watch server server/index.js",
"test-server": "jest ./server/**/*.test.js",
"heroku-postbuild": "yarn run build",
"timezones": "node scripts/parse-iana-timezones.js",
"translate": "node scripts/translations.js"
Expand Down
31 changes: 23 additions & 8 deletions scripts/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const run = () => {
process.on('exit', code => {
console.log(`
${chalk.bold.red(`You don't have required .env file!`)}
${chalk.bold.red(`You don't have required .env file!`)}
Some environment variables are required before starting Flex template for web. You can create the .env file and configure the variables by running ${chalk.cyan.bold(
'yarn run config'
Expand All @@ -35,7 +35,7 @@ Some environment variables are required before starting Flex template for web. Y
}
} else if (hasEnvFile) {
console.log(`
${chalk.bold.green('.env file already exists!')}
${chalk.bold.green('.env file already exists!')}
Remember to restart the application after editing the environment variables! You can also edit environment variables by editing the .env file directly in your text editor.
`);

Expand Down Expand Up @@ -65,7 +65,7 @@ Remember to restart the application after editing the environment variables! You
name: 'createEmptyEnv',
message: `Do you want to configure required environment variables now?
${chalk.dim(
`If you don't set up variables now .env file is created based on .env-template file. The application will not work correctly without Flex client ID, Stripe publishable API key and MapBox acces token.
`If you don't set up variables now .env file is created based on .env-template file. The application will not work correctly without Flex client ID, Flex client secret, Stripe publishable API key and MapBox acces token.
We recommend setting up the required variables before starting the application!`
)}`,
default: true,
Expand Down Expand Up @@ -102,6 +102,10 @@ const mandatoryVariables = settings => {
settings.REACT_APP_SHARETRIBE_SDK_CLIENT_ID !== 'change-me'
? { default: settings.REACT_APP_SHARETRIBE_SDK_CLIENT_ID }
: {};
const clientSecretDefaultMaybe =
settings && settings.SHARETRIBE_SDK_CLIENT_SECRET !== ''
? { default: settings.SHARETRIBE_SDK_CLIENT_SECRET }
: {};
const stripeDefaultMaybe =
settings && settings.REACT_APP_STRIPE_PUBLISHABLE_KEY !== ''
? { default: settings.REACT_APP_STRIPE_PUBLISHABLE_KEY }
Expand Down Expand Up @@ -132,6 +136,17 @@ ${chalk.dim(
},
...clientIdDefaultMaybe,
},
{
type: 'input',
name: 'SHARETRIBE_SDK_CLIENT_SECRET',
message: `What is your Flex client secret?
${chalk.dim(
`Client Secret is needed for privileged transitions with Flex API.
Client secret is connected to client ID. You can find your client secret from Flex Console.`
)}
`,
...clientSecretDefaultMaybe,
},
{
type: 'input',
name: 'REACT_APP_STRIPE_PUBLISHABLE_KEY',
Expand Down Expand Up @@ -166,7 +181,7 @@ If you don't set the Mapbox key, the map components won't work in the applicatio
message: `What is your marketplace currency?
${chalk.dim(
'The currency used in the Marketplace must be in ISO 4217 currency code. For example USD, EUR, CAD, AUD, etc. The default value is USD.'
)}
)}
`,
default: function() {
return currencyDefault;
Expand Down Expand Up @@ -211,7 +226,7 @@ const advancedSettings = settings => {
{
type: 'input',
name: 'REACT_APP_CANONICAL_ROOT_URL',
message: `What is your canonical root URL?
message: `What is your canonical root URL?
${chalk.dim(
'Canonical root URL of the marketplace is needed for social media sharing and SEO optimization. When developing the template application locally URL is usually http://localhost:3000'
)}
Expand Down Expand Up @@ -242,7 +257,7 @@ ${chalk.dim(
message: `Do you want to enable default search suggestions?
${chalk.dim(
'This setting enables the Default Search Suggestions in location autocomplete search input. The default value for this setting is true.'
)}
)}
`,
default: searchesDefault,
when: function(answers) {
Expand Down Expand Up @@ -292,13 +307,13 @@ const askQuestions = settings => {
*/
const showSuccessMessage = () => {
console.log(`
${chalk.green.bold('.env file saved succesfully!')}
${chalk.green.bold('.env file saved succesfully!')}
Start the Flex template application by running ${chalk.bold.cyan('yarn run dev')}
Note that the .env file is a hidden file so it might not be visible directly in directory listing. If you want to update the environment variables run ${chalk.cyan.bold(
'yarn run config'
)} again or edit the .env file directly. Remember to restart the application after editing the environment variables!
)} again or edit the .env file directly. Remember to restart the application after editing the environment variables!
`);
};

Expand Down
123 changes: 123 additions & 0 deletions server/api-util/currency.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
const Decimal = require('decimal.js');
const has = require('lodash/has');
const { types } = require('sharetribe-flex-sdk');
const { Money } = types;

/** Helper functions for handling currency */

// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER
// https://stackoverflow.com/questions/26380364/why-is-number-max-safe-integer-9-007-199-254-740-991-and-not-9-007-199-254-740-9
const MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER || -1 * (2 ** 53 - 1);
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 2 ** 53 - 1;

const isSafeNumber = decimalValue => {
if (!(decimalValue instanceof Decimal)) {
throw new Error('Value must be a Decimal');
}
return decimalValue.gte(MIN_SAFE_INTEGER) && decimalValue.lte(MAX_SAFE_INTEGER);
};

// See: https://en.wikipedia.org/wiki/ISO_4217
// See: https://stripe.com/docs/currencies
const subUnitDivisors = {
AUD: 100,
CAD: 100,
CHF: 100,
CNY: 100,
DKK: 100,
EUR: 100,
GBP: 100,
HKD: 100,
INR: 100,
JPY: 1,
MXN: 100,
NOK: 100,
NZD: 100,
SEK: 100,
SGD: 100,
USD: 100,
};

// Get the minor unit divisor for the given currency
exports.unitDivisor = currency => {
if (!has(subUnitDivisors, currency)) {
throw new Error(
`No minor unit divisor defined for currency: ${currency} in /server/api-util/currency.js`
);
}
return subUnitDivisors[currency];
};

// Divisor can be positive value given as Decimal, Number, or String
const convertDivisorToDecimal = divisor => {
try {
const divisorAsDecimal = new Decimal(divisor);
if (divisorAsDecimal.isNegative()) {
throw new Error(`Parameter (${divisor}) must be a positive number.`);
}
return divisorAsDecimal;
} catch (e) {
throw new Error(`Parameter (${divisor}) must present a number.`, e);
}
};

// Detect if the given value is a goog.math.Long object
// See: https://google.github.io/closure-library/api/goog.math.Long.html
const isGoogleMathLong = value => {
return typeof value === 'object' && isNumber(value.low_) && isNumber(value.high_);
};

/**
* Gets subunit amount from Money object and returns it as Decimal.
*
* @param {Money} value
*
* @return {Number} converted value
*/
exports.getAmountAsDecimalJS = value => {
if (!(value instanceof Money)) {
throw new Error('Value must be a Money type');
}
let amount;

if (isGoogleMathLong(value.amount)) {
// TODO: temporarily also handle goog.math.Long values created by
// the Transit tooling in the Sharetribe JS SDK. This should be
// removed when the value.amount will be a proper Decimal type.

// eslint-disable-next-line no-console
console.warn('goog.math.Long value in money amount:', value.amount, value.amount.toString());

amount = new Decimal(value.amount.toString());
} else {
amount = new Decimal(value.amount);
}

if (!isSafeNumber(amount)) {
throw new Error(
`Cannot represent money minor unit value ${amount.toString()} safely as a number`
);
}

return amount;
};

/**
* Converts value from DecimalJS to plain JS Number.
* This also checks that Decimal.js value (for Money/amount)
* is not too big or small for JavaScript to handle.
*
* @param {Decimal} value
*
* @return {Number} converted value
*/
exports.convertDecimalJSToNumber = decimalValue => {
if (!isSafeNumber(decimalValue)) {
throw new Error(
`Cannot represent Decimal.js value ${decimalValue.toString()} safely as a number`
);
}

return decimalValue.toNumber();
};
Loading

0 comments on commit bdbcbb4

Please sign in to comment.