Skip to content

Commit

Permalink
feat(process): added cli parameters for importing directly to sphere
Browse files Browse the repository at this point in the history
  • Loading branch information
junajan committed Aug 24, 2016
1 parent cd9050d commit 66b0dee
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 42 deletions.
4 changes: 2 additions & 2 deletions Gruntfile.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ module.exports = (grunt) ->
stderr: true
failOnError: true
coverage:
command: 'istanbul cover jasmine-node --captureExceptions test && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage'
command: './node_modules/.bin/istanbul cover jasmine-node --captureExceptions test && cat ./coverage/lcov.info | ./node_modules/.bin/coveralls && rm -rf ./coverage'
jasmine:
command: 'jasmine-node --captureExceptions test'
command: './node_modules/.bin/jasmine-node --captureExceptions test'
publish:
command: 'npm publish'

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"node": ">= 0.10.0"
},
"scripts": {
"test": "grunt coverage"
"test": "grunt coverage",
"build": "grunt build"
},
"dependencies": {
"bluebird": "2.9.x",
Expand All @@ -49,6 +50,7 @@
"optimist": "0.6.x",
"sphere-node-sdk": "1.3.x",
"sphere-node-utils": "0.7.0",
"sphere-product-type-import": "^1.1.0",
"underscore": "1.8.x",
"underscore.string": "3.1.x"
},
Expand Down
94 changes: 94 additions & 0 deletions src/coffee/product-type-import.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
_ = require 'underscore'
Promise = require 'bluebird'
ProductTypeImport = require 'sphere-product-type-import'
{ExtendedLogger, ProjectCredentialsConfig} = require 'sphere-node-utils'
package_json = require '../package.json'

class ProductTypeImporter

###
Prepare for importing, initialize used modules
###
init: (argv) ->
@argv = argv
@logOptions =
name: "#{package_json.name}-#{package_json.version}"
silent: !! argv.logSilent
streams: [
{ level: 'error', stream: process.stderr }
{ level: argv.logLevel || 'info', path: (argv.logDir || '.') + "/#{package_json.name}.log" }
]

@logger = new ExtendedLogger
additionalFields:
'project_key': argv.projectKey
logConfig: @logOptions
if argv.logSilent
@logger.bunyanLogger.trace = -> # noop
@logger.bunyanLogger.debug = -> # noop

@_ensureProductTypeImporter @argv, @logger
.then (sphereImporter) ->
@sphereImporter = sphereImporter
Promise.resolve @

###
Create sphere credentials config from command line arguments
###
_ensureCredentials: (argv) ->
credentialsPromise = null

if argv.accessToken
credentialsPromise = Promise.resolve
config:
project_key: argv.projectKey
access_token: argv.accessToken
else
credentialsPromise = ProjectCredentialsConfig.create()
.then (credentials) ->
Promise.resolve
config: credentials.enrichCredentials
project_key: argv.projectKey
client_id: argv.clientId
client_secret: argv.clientSecret

credentialsPromise.then (credentials) ->
options = _.extend credentials,
timeout: argv.timeout
user_agent: "#{package_json.name} - #{package_json.version}"

options.host = argv.sphereHost if argv.sphereHost
options.protocol = argv.sphereProtocol if argv.sphereProtocol
if argv.sphereAuthHost
options.oauth_host = argv.sphereAuthHost
options.rejectUnauthorized = false
options.oauth_protocol = argv.sphereAuthProtocol if argv.sphereAuthProtocol

config =
sphereClientConfig: options

Promise.resolve config

###
Create sphere product import class
###
_ensureProductTypeImporter: (argv, logger) ->
@_ensureCredentials argv
.then (credentials) ->
new ProductTypeImport.default(logger, credentials)

###
Import product types using sphere product import tool
###
import: (data) ->
console.log "Importing product types to sphere"
Promise.map data.productTypes, (productType) ->
@sphereImporter.importProductType productType
.then (res) ->
console.log "Imported product type", res.name
Promise.resolve res
.catch (e) ->
console.error "Oops, something went wrong when importing product type #{productType.name} to sphere"
Promise.reject e

module.exports = ProductTypeImporter
115 changes: 76 additions & 39 deletions src/coffee/run.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ fs = Promise.promisifyAll require('fs')
Csv = require 'csv'
JSZip = require 'jszip'
ProductTypeGenerator = require './product-type-generator'
ProductTypeImporter = require './product-type-import'

argv = require('optimist')
.usage('Usage: $0 --types [CSV] --attributes [CSV] --target [folder] --withRetailer --zip --zipFileName [name]')
Expand All @@ -13,6 +14,26 @@ argv = require('optimist')
.describe('withRetailer', 'whether to generate an extra file for master<->retailer support with a "mastersku" attribute or not')
.describe('zip', 'whether to zip the target folder or not')
.describe('zipFileName', 'the zipped file name (without extension)')

# product type import tool config
.describe('projectKey', 'your SPHERE.IO project-key')
.describe('clientId', 'your OAuth client id for the SPHERE.IO API')
.describe('clientSecret', 'your OAuth client secret for the SPHERE.IO API')
.describe('accessToken', 'an OAuth access token for the SPHERE.IO API')
.describe('sphereHost', 'SPHERE.IO API host to connecto to')
.describe('sphereProtocol', 'SPHERE.IO API protocol to connect to')
.describe('sphereAuthHost', 'SPHERE.IO OAuth host to connect to')
.describe('sphereAuthProtocol', 'SPHERE.IO OAuth protocol to connect to')
.describe('sphereImport', 'import product types to SPHERE.IO')
.describe('logSilent', 'use console to print messages')
.describe('logDir', 'directory to store logs')
.describe('logLevel', 'log level for file logging')
.default('projectKey', false)
.default('sphereImport', false)
.default('logSilent', false)
.default('logDir', '.')
.default('logLevel', 'info')

.default('withRetailer', false)
.default('zip', false)
.default('zipFileName', 'generated-product-types')
Expand All @@ -21,11 +42,11 @@ argv = require('optimist')
.alias('target', 'td')
.alias('withRetailer', 'r')
.boolean('withRetailer')
.boolean('sphereImport')
.boolean('zip')
.demand(['types', 'attributes', 'target'])
.argv


###
Reads a CSV file by given path and returns a promise for the result.
@param {string} path The path of the CSV file.
Expand Down Expand Up @@ -54,6 +75,50 @@ zipFiles = (path, filename) ->
buffer = zip.generate type: 'nodebuffer'
fs.writeFileAsync "#{path}/#{filename}.zip", buffer, 'utf8'

saveProductTypes = (result) ->
console.log 'About to write files...'
if _.isEmpty result.productTypes
Promise.reject new Error('We couldn\'t generate any file based on the given data. Please check your CSVs.')
else
Promise.map result.productTypes, (productType) ->
writeFileAsync productType, argv.target
.then ->
console.log "Generated #{_.size result.productTypes} files for normal product-types"
if argv.withRetailer
if _.isEmpty result.productTypesForRetailer
Promise.reject new Error('We couldn\'t generate any file for master<->retailer support based on the given data. Please check your CSVs.')
else
Promise.map result.productTypesForRetailer, (productType) ->
writeFileAsync productType, argv.target, 'retailer-product-type'
.then ->
console.log "Generated #{_.size result.productTypesForRetailer} files for retailer product-types"
Promise.resolve()
else
Promise.resolve()
.then ->
console.log "Finished generating files, checking results in target folder: #{argv.target}"
fs.readdirAsync argv.target
.then (files) ->
if not argv.zip
Promise.resolve()
else
jsonFiles = _.filter files, (file) -> file.match(/\.json/)
if _.isEmpty jsonFiles
Promise.reject "No files were written in target folder #{argv.target}"
else
console.log "Found #{_.size jsonFiles} files in target folder #{argv.target}"
console.log "Zipping files as #{argv.zipFileName} name"
zipFiles argv.target, argv.zipFileName
.catch (e) ->
console.error "Oops, there was an error reading the files in target folder #{argv.target}: #{e.message}"
process.exit 1

importSphereProductTypes = (data) ->
importer = new ProductTypeImporter
importer.init(argv)
.then ->
importer.import data

console.log 'About to read CSV files...'
Promise.all [readCsvAsync(argv.types), readCsvAsync(argv.attributes)]
.spread (types, attributes) ->
Expand All @@ -62,47 +127,19 @@ Promise.all [readCsvAsync(argv.types), readCsvAsync(argv.attributes)]
# TODO: make it async
generator.run types, attributes, argv.target, argv.withRetailer
.then (result) ->
console.log 'About to write files...'
if _.isEmpty result.productTypes
Promise.reject new Error('We couldn\'t generate any file based on the given data. Please check your CSVs.')
if argv.sphereImport
importSphereProductTypes result
.catch (e) ->
console.error "Ending with an error: #{e.message}"
process.exit 1
else
Promise.map result.productTypes, (productType) ->
writeFileAsync productType, argv.target
.then ->
console.log "Generated #{_.size result.productTypes} files for normal product-types"
if argv.withRetailer
if _.isEmpty result.productTypesForRetailer
Promise.reject new Error('We couldn\'t generate any file for master<->retailer support based on the given data. Please check your CSVs.')
else
Promise.map result.productTypesForRetailer, (productType) ->
writeFileAsync productType, argv.target, 'retailer-product-type'
.then ->
console.log "Generated #{_.size result.productTypesForRetailer} files for retailer product-types"
Promise.resolve()
else
Promise.resolve()
.then ->
console.log "Finished generating files, checking results in target folder: #{argv.target}"
fs.readdirAsync argv.target
.then (files) ->
jsonFiles = _.filter files, (file) -> file.match(/\.json/)
if _.isEmpty jsonFiles
Promise.reject "No files were written in target folder #{argv.target}"
else
console.log "Found #{_.size jsonFiles} files in target folder #{argv.target}"
if argv.zip
console.log "Zipping files as #{argv.zipFileName} name"
zipFiles argv.target, argv.zipFileName
else
Promise.resolve()
.catch (e) ->
console.error "Oops, there was an error reading the files in target folder #{argv.target}: #{e.message}"
process.exit 1
.then ->
console.log 'Execution successfully finished'
process.exit 0
saveProductTypes result
.then ->
console.log 'Execution successfully finished'
process.exit 0
.catch (e) ->
console.error "Oops, something went wrong: #{e.message}"
console.dir e.stack
process.exit 1
.catch (e) ->
console.error "Could not read CSV files: #{e.message}"
Expand Down
File renamed without changes.
110 changes: 110 additions & 0 deletions src/spec/product-type-importer.spec.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
Promise = require 'bluebird'
ProductTypeImporter = require '../lib/product-type-import'
{SphereClient} = require 'sphere-node-sdk'

errMissingCredentials = 'Missing configuration in env variables'

argv =
clientId: process.env.SPHERE_CLIENT_ID
clientSecret: process.env.SPHERE_CLIENT_SECRET
projectKey: process.env.SPHERE_PROJECT_KEY
logSilent: true

testProductType = {
"name": "top_and_shirts",
"description": "Tops & Shirts",
"attributes": [
{
"name": "designer",
"label": {
"de": "Designer",
"en": "designer"
},
"type": {
"name": "text"
},
"attributeConstraint": "SameForAll",
"isRequired": false,
"isSearchable": false,
"inputHint": "SingleLine"
},
{
"name": "materialComposition",
"label": {
"de": "Zusammensetzung Material",
"en": "material composition"
},
"type": {
"name": "ltext"
},
"attributeConstraint": "None",
"isRequired": false,
"isSearchable": false,
"inputHint": "MultiLine"
}
]
}

describe 'ProductTypeImporter', ->
importer = null
sphereClient = null

beforeEach (done) ->
expect(argv.clientId).toBeDefined errMissingCredentials
expect(argv.clientSecret).toBeDefined errMissingCredentials
expect(argv.projectKey).toBeDefined errMissingCredentials

config =
config:
'client_id': argv.clientId
'client_secret': argv.clientSecret
'project_key': argv.projectKey
stats:
includeHeaders: true
maskSensitiveHeaderData: true
timeout: 360000

sphereClient = new SphereClient config
importer = new ProductTypeImporter
importer.init(argv)
.then ->
sphereClient.productTypes.fetch()
.then (res) ->
console.log "Deleting old product types", res.body.results.length
Promise.map res.body.results, (productType) ->
sphereClient.productTypes.byId(productType.id)
.delete(productType.version)
.then ->
done()
.catch (e) ->
done(e)

it 'should import product type', (done) ->
expect(importer).toBeDefined()
expect(sphereClient).toBeDefined()

sphereClient.productTypes.fetch()
.then (res) ->
expect(res.body.results.length).toEqual 0
console.log "Importing product type using importer"
importer.import {productTypes: [testProductType]}
.then ->
console.log "Product type imported - verifiing result"
sphereClient.productTypes.fetch()
.then (res) ->
expect(res.body.results.length).toEqual 1

done()
.catch (e) ->
done(e)

it 'should not import wrong product type', (done) ->
expect(importer).toBeDefined()
expect(sphereClient).toBeDefined()

delete testProductType.name
importer.import {productTypes: [testProductType]}
.then ->
done "Importer wrong product type"
.catch ->
done()

0 comments on commit 66b0dee

Please sign in to comment.