├── .editorconfig
├── .gitignore
├── .runner.conf
├── Dockerfile
├── Gopkg.lock
├── Gopkg.toml
├── LICENSE.md
├── README.md
├── api
├── context
│ ├── context.go
│ ├── context_test.go
│ └── doc.go
├── decl.go
├── doc.go
├── helpers.go
├── interfaces.go
├── manager.go
├── rest
│ ├── decl.go
│ ├── doc.go
│ ├── i_application_context.go
│ ├── i_restservice.go
│ └── init.go
└── session
│ ├── decl.go
│ ├── doc.go
│ ├── service.go
│ ├── service_filesystem.go
│ ├── service_memcache.go
│ ├── service_memory.go
│ ├── service_redis.go
│ ├── session.go
│ └── session_test.go
├── app
├── actors
│ ├── blog
│ │ ├── init.go
│ │ └── post
│ │ │ ├── api.go
│ │ │ ├── decl.go
│ │ │ ├── i_post.go
│ │ │ ├── init.go
│ │ │ └── post_api_test.go
│ ├── cart
│ │ ├── add_item_test.go
│ │ ├── api.go
│ │ ├── config.go
│ │ ├── decl.go
│ │ ├── i_cart.go
│ │ ├── i_cart_item.go
│ │ ├── i_model.go
│ │ ├── i_storable.go
│ │ └── init.go
│ ├── category
│ │ ├── api.go
│ │ ├── category.i_category.go
│ │ ├── category.i_listable.go
│ │ ├── category.i_media.go
│ │ ├── category.i_model.go
│ │ ├── category.i_object.go
│ │ ├── category.i_storable.go
│ │ ├── collection.i_category_collection.go
│ │ ├── collection.i_collection.go
│ │ ├── collection.i_model.go
│ │ ├── decl.go
│ │ └── init.go
│ ├── checkout
│ │ ├── api.go
│ │ ├── config.go
│ │ ├── decl.go
│ │ ├── i_checkout.go
│ │ ├── i_model.go
│ │ ├── i_object.go
│ │ ├── init.go
│ │ ├── internal.go
│ │ └── price_adjustment_test.go
│ ├── cms
│ │ ├── block
│ │ │ ├── api.go
│ │ │ ├── block.i_cms_block.go
│ │ │ ├── block.i_listable.go
│ │ │ ├── block.i_model.go
│ │ │ ├── block.i_object.go
│ │ │ ├── block.i_storable.go
│ │ │ ├── collection.i_cms_block_collection.go
│ │ │ ├── collection.i_collection.go
│ │ │ ├── collection.i_model.go
│ │ │ ├── decl.go
│ │ │ └── init.go
│ │ ├── init.go
│ │ ├── media
│ │ │ ├── api.go
│ │ │ ├── decl.go
│ │ │ ├── helpers.go
│ │ │ └── init.go
│ │ └── page
│ │ │ ├── api.go
│ │ │ ├── collection.i_cms_page_collection.go
│ │ │ ├── collection.i_collection.go
│ │ │ ├── collection.i_model.go
│ │ │ ├── decl.go
│ │ │ ├── init.go
│ │ │ ├── page.i_cms_page.go
│ │ │ ├── page.i_listable.go
│ │ │ ├── page.i_model.go
│ │ │ ├── page.i_object.go
│ │ │ └── page.i_storable.go
│ ├── discount
│ │ ├── coupon
│ │ │ ├── api.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_price_adjustment.go
│ │ │ └── init.go
│ │ ├── giftcard
│ │ │ ├── api.go
│ │ │ ├── config.go
│ │ │ ├── cron.go
│ │ │ ├── decl.go
│ │ │ ├── handlers.go
│ │ │ ├── i_price_adjustment.go
│ │ │ ├── i_shipping_method.go
│ │ │ └── init.go
│ │ └── saleprice
│ │ │ ├── api.go
│ │ │ ├── collection.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── delegate.go
│ │ │ ├── init.go
│ │ │ ├── price_adjustment.go
│ │ │ └── saleprice.go
│ ├── order
│ │ ├── api.go
│ │ ├── config.go
│ │ ├── decl.go
│ │ ├── init.go
│ │ ├── internal.go
│ │ ├── item.i_object.go
│ │ ├── item.i_order_item.go
│ │ ├── item_collection.i_listable.go
│ │ ├── item_collection.i_model.go
│ │ ├── item_collection.i_order_item_collection.go
│ │ ├── order.i_listable.go
│ │ ├── order.i_model.go
│ │ ├── order.i_object.go
│ │ ├── order.i_order.go
│ │ ├── order.i_storable.go
│ │ ├── order_collection.i_listable.go
│ │ ├── order_collection.i_model.go
│ │ └── order_collection.i_order_collection.go
│ ├── other
│ │ ├── emma
│ │ │ ├── api.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── emma.go
│ │ │ ├── handlers.go
│ │ │ └── init.go
│ │ ├── friendmail
│ │ │ ├── api.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ └── init.go
│ │ ├── grouping
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── handlers.go
│ │ │ └── init.go
│ │ ├── mailchimp
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── handlers.go
│ │ │ ├── init.go
│ │ │ └── mailchimp_test.go
│ │ ├── shipstation
│ │ │ ├── api.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── init.go
│ │ │ ├── internal.go
│ │ │ └── shipstation_test.go
│ │ ├── trustpilot
│ │ │ ├── api.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── handlers.go
│ │ │ ├── handlers_test.go
│ │ │ └── init.go
│ │ └── vantagepoint
│ │ │ ├── actors
│ │ │ ├── decl.go
│ │ │ ├── diskstorage.go
│ │ │ ├── inventorycsv.go
│ │ │ ├── test
│ │ │ │ ├── inventorycsv_test.go
│ │ │ │ └── uploadsprocessor_test.go
│ │ │ ├── uploadsprocessor.go
│ │ │ └── uploadsprocessor_test.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── init.go
│ │ │ ├── internal.go
│ │ │ └── schedule.go
│ ├── payment
│ │ ├── authorizenet
│ │ │ ├── api.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_payment_method-rest.go
│ │ │ ├── i_payment_method.go
│ │ │ ├── init.go
│ │ │ ├── internal.go
│ │ │ └── rest_method_integration_test.go
│ │ ├── braintree
│ │ │ ├── cc_method_integration_test.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_payment_method-cc.go
│ │ │ ├── init.go
│ │ │ └── internal.go
│ │ ├── checkmo
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_payment_method.go
│ │ │ └── init.go
│ │ ├── paypal
│ │ │ ├── api.go
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_payment_method-express.go
│ │ │ ├── i_payment_method-payflow.go
│ │ │ ├── i_payment_method-rest.go
│ │ │ ├── init.go
│ │ │ └── internal.go
│ │ ├── stripe
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_payment_method.go
│ │ │ ├── init.go
│ │ │ └── internal.go
│ │ └── zeropay
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_payment_method.go
│ │ │ └── init.go
│ ├── product
│ │ ├── api.go
│ │ ├── apply_options_test.go
│ │ ├── collection.go
│ │ ├── decl.go
│ │ ├── init.go
│ │ ├── product.go
│ │ └── review
│ │ │ ├── api.go
│ │ │ ├── decl.go
│ │ │ ├── init.go
│ │ │ └── review_api_test.go
│ ├── reporting
│ │ ├── api.go
│ │ ├── decl.go
│ │ └── init.go
│ ├── rts
│ │ ├── api.go
│ │ ├── config.go
│ │ ├── decl.go
│ │ ├── handlers.go
│ │ ├── init.go
│ │ └── internal.go
│ ├── seo
│ │ ├── api.go
│ │ ├── collection.go
│ │ ├── decl.go
│ │ ├── helpers.go
│ │ ├── init.go
│ │ ├── listable.go
│ │ ├── seo.go
│ │ └── seo_test.go
│ ├── shipping
│ │ ├── fedex
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_shipping_method.go
│ │ │ └── init.go
│ │ ├── flatrate
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_shipping_method.go
│ │ │ └── init.go
│ │ ├── flatweight
│ │ │ ├── config.go
│ │ │ ├── config_test.go
│ │ │ ├── decl.go
│ │ │ ├── i_shipping_method.go
│ │ │ └── init.go
│ │ └── usps
│ │ │ ├── config.go
│ │ │ ├── decl.go
│ │ │ ├── i_shipping_method.go
│ │ │ └── init.go
│ ├── stock
│ │ ├── api.go
│ │ ├── collection.go
│ │ ├── config.go
│ │ ├── decl.go
│ │ ├── helpers.go
│ │ ├── i_delegate.go
│ │ ├── i_listable.go
│ │ ├── i_stock.go
│ │ ├── init.go
│ │ └── stock_test.go
│ ├── subscription
│ │ ├── api.go
│ │ ├── collection.i_collection.go
│ │ ├── collection.i_model.go
│ │ ├── collection.i_subscription_collection.go
│ │ ├── config.go
│ │ ├── cron.go
│ │ ├── decl.go
│ │ ├── handlers.go
│ │ ├── init.go
│ │ ├── internal.go
│ │ ├── subscription.i_listable.go
│ │ ├── subscription.i_model.go
│ │ ├── subscription.i_object.go
│ │ ├── subscription.i_storable.go
│ │ └── subscription.i_subscription.go
│ ├── swatch
│ │ ├── api.go
│ │ ├── decl.go
│ │ └── init.go
│ ├── tax
│ │ ├── api.go
│ │ ├── decl.go
│ │ ├── i_price_adjustment.go
│ │ └── init.go
│ ├── visitor
│ │ ├── address
│ │ │ ├── address.i_listable.go
│ │ │ ├── address.i_model.go
│ │ │ ├── address.i_object.go
│ │ │ ├── address.i_storable.go
│ │ │ ├── address.i_visitor_address.go
│ │ │ ├── api.go
│ │ │ ├── collection.i_collection.go
│ │ │ ├── collection.i_model.go
│ │ │ ├── collection.i_visitor_address_collection.go
│ │ │ ├── decl.go
│ │ │ └── init.go
│ │ ├── api.go
│ │ ├── collection.i_collection.go
│ │ ├── collection.i_model.go
│ │ ├── collection.i_visitor_collection.go
│ │ ├── config.go
│ │ ├── decl.go
│ │ ├── init.go
│ │ ├── token
│ │ │ ├── api.go
│ │ │ ├── collection.i_collection.go
│ │ │ ├── collection.i_model.go
│ │ │ ├── collection.i_visitor_card_collection.go
│ │ │ ├── decl.go
│ │ │ ├── init.go
│ │ │ ├── token.i_model.go
│ │ │ ├── token.i_object.go
│ │ │ ├── token.i_storable.go
│ │ │ └── token.i_visitor_card.go
│ │ ├── visitor.i_listable.go
│ │ ├── visitor.i_model.go
│ │ ├── visitor.i_object.go
│ │ ├── visitor.i_storable.go
│ │ └── visitor.i_visitor.go
│ └── xdomain
│ │ ├── api.go
│ │ ├── decl.go
│ │ └── init.go
├── api.go
├── config.go
├── decl.go
├── doc.go
├── helpers.go
├── helpers
│ ├── attributes
│ │ ├── custom.go
│ │ ├── decl.go
│ │ ├── external.go
│ │ ├── external_test.go
│ │ ├── init.go
│ │ └── other_test.go
│ └── objectref
│ │ ├── decl.go
│ │ ├── i_object.go
│ │ └── i_storable.go
├── init.go
├── manager.go
└── models
│ ├── blog
│ └── post
│ │ ├── helpers.go
│ │ └── interfaces.go
│ ├── cart
│ ├── helpers.go
│ └── interfaces.go
│ ├── category
│ ├── helpers.go
│ └── interfaces.go
│ ├── checkout
│ ├── decl.go
│ ├── helpers.go
│ ├── interfaces.go
│ └── manager.go
│ ├── cms
│ ├── helpers.go
│ └── interfaces.go
│ ├── decl.go
│ ├── discount
│ └── saleprice
│ │ ├── decl.go
│ │ ├── helpers.go
│ │ └── interfaces.go
│ ├── helpers.go
│ ├── interfaces.go
│ ├── manager.go
│ ├── order
│ ├── helpers.go
│ └── interfaces.go
│ ├── product
│ ├── helpers.go
│ ├── interfaces.go
│ └── manager.go
│ ├── seo
│ ├── helpers.go
│ ├── interfaces.go
│ └── manager.go
│ ├── stock
│ ├── helpers.go
│ └── interfaces.go
│ ├── subscription
│ ├── decl.go
│ ├── helpers.go
│ └── interfaces.go
│ └── visitor
│ ├── helpers.go
│ └── interfaces.go
├── basebuild
├── build.go
├── build_mongo.go
├── build_mysql.go
├── build_postgres.go
├── build_sqlite.go
└── doc.go
├── bin
├── bootstrap.sh
├── build-docker-image.sh
├── deploy.sh
├── docker-entrypoint.sh
├── make.sh
├── mongo-dump-csv.sh
├── prod_deploy.sh
├── slack_notifier.py
└── update-docs.sh
├── db
├── dbconnector.go
├── doc.go
├── helpers.go
├── interfaces.go
├── manager.go
├── mongo
│ ├── collection.i_dbcollection.go
│ ├── collection.internal.go
│ ├── decl.go
│ ├── doc.go
│ ├── engine.i_dbconnector.go
│ ├── engine.i_dbengine.go
│ ├── init.go
│ └── utils.go
├── mysql
│ ├── collection.i_dbcollection.go
│ ├── collection.internal.go
│ ├── dbcollection_test.go
│ ├── decl.go
│ ├── doc.go
│ ├── engine.i_dbconnector.go
│ ├── engine.i_dbengine.go
│ ├── init.go
│ └── internal.go
├── postgres
│ ├── collection.i_dbcollection.go
│ ├── collection.internal.go
│ ├── decl.go
│ ├── doc.go
│ ├── engine.i_dbconnector.go
│ ├── engine.i_dbengine.go
│ ├── init.go
│ ├── internal.go
│ └── postgres_test.go
├── sqlite
│ ├── collection.i_dbcollection.go
│ ├── collection.internal.go
│ ├── dbcollection_test.go
│ ├── decl.go
│ ├── doc.go
│ ├── engine.i_dbconnector.go
│ ├── engine.i_dbengine.go
│ ├── init.go
│ └── internal.go
└── test.go
├── doc.go
├── env
├── config
│ ├── api.go
│ ├── decl.go
│ ├── doc.go
│ ├── i_config.go
│ ├── i_impex_model.go
│ └── init.go
├── cron
│ ├── api.go
│ ├── decl.go
│ ├── doc.go
│ ├── i_schedule.go
│ ├── i_scheduler.go
│ └── init.go
├── doc.go
├── errorbus
│ ├── config.go
│ ├── decl.go
│ ├── doc.go
│ ├── i_error_bus.go
│ ├── i_ottemo_error.go
│ └── init.go
├── eventbus
│ ├── decl.go
│ ├── doc.go
│ ├── i_event_bus.go
│ └── init.go
├── helpers.go
├── ini
│ ├── decl.go
│ ├── doc.go
│ ├── i_iniconfig.go
│ └── init.go
├── interfaces.go
├── logger
│ ├── config.go
│ ├── decl.go
│ ├── doc.go
│ ├── i_logger.go
│ └── init.go
└── manager.go
├── foundation.conf
├── impex
├── api.go
├── commands.go
├── csv.go
├── decl.go
├── doc.go
├── helpers.go
├── init.go
├── interfaces.go
└── manager.go
├── main.go
├── media
├── doc.go
├── fsmedia
│ ├── api.go
│ ├── config.go
│ ├── decl.go
│ ├── doc.go
│ ├── i_media_storage.go
│ ├── image.go
│ └── init.go
├── interfaces.go
└── manager.go
├── ottemo.sample.ini
├── swagger.json
├── test
├── checkout_test.go
├── doc.go
├── generic.go
├── helpers.go
└── product_test.go
├── tests
└── jmeter
│ ├── address.txt
│ ├── oneStepCheckout.jmx
│ └── visitor.txt
└── utils
├── crypt.go
├── datatypes.go
├── doc.go
├── generic.go
├── generic_test.go
├── json.go
├── rounding_test.go
├── sorting.go
├── sorting_test.go
├── sync.go
├── sync_test.go
├── templates.go
└── timezones.go
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 4
6 | end_of_line = LF
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.go]
12 | indent_style = tab
13 | indent_size = 8
14 |
15 | [*.{yml,json}]
16 | indent_style=space
17 | indent_size=2
18 |
19 | [*.md]
20 | trim_trailing_whitespace = false
21 |
22 | [*.{html,js,css,scss,xml}]
23 | indent_size=2
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 | *.gz
6 |
7 | # Folders
8 | _obj
9 | _test
10 | tags
11 | .tags
12 | .idea
13 | tmp
14 | release
15 | var
16 | vendor
17 | .vagrant
18 | .vscode
19 | kg-media
20 |
21 | # Architecture specific extensions/prefixes
22 | *.[568vq]
23 | [568vq].out
24 |
25 | *.cgo1.go
26 | *.cgo2.c
27 | _cgo_defun.c
28 | _cgo_gotypes.go
29 | _cgo_export.*
30 |
31 | _testmain.go
32 | npm-debug.log
33 |
34 | *.exe
35 | gin-bin
36 | foundation
37 | commerce
38 |
39 | # Configuration files
40 | *.iml
41 | glide.lock
42 |
43 | # test dbs
44 | db/sqlite/test/ottemo.db
45 | ottemo.db
46 | ottemo.ini
47 | ottemo.ini.bak
48 | .DS_Store
49 |
--------------------------------------------------------------------------------
/.runner.conf:
--------------------------------------------------------------------------------
1 | root: .
2 | tmp_path: ./tmp
3 | build_name: runner-build
4 | build_log: runner-build-errors.log
5 | valid_ext: .go, .tpl, .tmpl, .html
6 | ignored: assets, tmp, media, tags
7 | build_delay: 600
8 | colors: 1
9 | log_color_main: cyan
10 | log_color_build: yellow
11 | log_color_runner: green
12 | log_color_watcher: magenta
13 | log_color_app:
14 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.5
2 |
3 | RUN apk add --no-cache ca-certificates gawk
4 |
5 | RUN mkdir -pv /home/ottemo/commerce
6 | RUN mkdir -pv /home/ottemo/commerce/var/log
7 | RUN mkdir -pv /home/ottemo/commerce/var/session
8 |
9 | COPY commerce /home/ottemo/commerce/
10 |
11 | # create links for proper logging
12 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/cron.log
13 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/events.log
14 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/paypal.log
15 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/rest.log
16 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/subscription.log
17 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/mongo.log
18 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/events.log
19 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/models.log
20 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/usps.log
21 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/impex.log
22 | RUN ln -sf /dev/stdout /home/ottemo/commerce/var/log/product
23 | RUN ln -sf /dev/stderr /home/ottemo/commerce/var/log/errors.log
24 |
25 | COPY bin/docker-entrypoint.sh /home/ottemo/commerce/
26 |
27 | EXPOSE 3000
28 | WORKDIR /home/ottemo/commerce
29 | CMD ./docker-entrypoint.sh
30 |
--------------------------------------------------------------------------------
/api/decl.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // Package global constants
8 | const (
9 | ConstRESTActionParameter = "action"
10 |
11 | ConstSessionKeyAdminRights = "adminRights" // session key used to flag that user have admin rights
12 | ConstSessionCookieName = "OTTEMOSESSION" // cookie name which should contain sessionID
13 | ConstSessionKeyTimeZone = "timeZone" // session key for setting time zone
14 |
15 | ConstGETAuthParamName = "auth"
16 | ConstConfigPathStoreRootLogin = "general.store.root_login"
17 | ConstConfigPathStoreRootPassword = "general.store.root_password"
18 |
19 | ConstErrorModule = "api"
20 | ConstErrorLevel = env.ConstErrorLevelHelper
21 | )
22 |
23 | // ApplicationContext is a type you can embed in your model for application context support
24 | type ApplicationContext struct{ InterfaceApplicationContext }
25 |
26 | // GetApplicationContext returns current application context or nil
27 | func (it *ApplicationContext) GetApplicationContext() InterfaceApplicationContext {
28 | return it.InterfaceApplicationContext
29 | }
30 |
31 | // SetApplicationContext assigns given application context to type
32 | func (it *ApplicationContext) SetApplicationContext(context InterfaceApplicationContext) error {
33 | it.InterfaceApplicationContext = context
34 | return nil
35 | }
36 |
37 | // StructRestRedirect is a structure you should return in API handler function if redirect needed
38 | type StructRestRedirect struct {
39 | Result interface{}
40 | Location string
41 |
42 | DoRedirect bool
43 | }
44 |
45 | // FuncAPIHandler is an API handler callback function
46 | type FuncAPIHandler func(context InterfaceApplicationContext) (interface{}, error)
47 |
48 | // FuncAPIResultHandler is an API result handler callback function.
49 | // It suppesod to be called by async API handler like api.APIAsyncHandler.
50 | type FuncAPIResultHandler func(context InterfaceApplicationContext, result interface{}, err error)
51 |
--------------------------------------------------------------------------------
/api/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package api represents API services abstraction. It provides a set of interfaces and helpers for a packages to extend
5 | application with new API endpoints as well as to interact with other components of application through them.
6 |
7 | As application components (actor packages) should not interact between them directly them should use this package to make
8 | indirect calls between the. Interaction with a session manager also should happen through this package.
9 | */
10 | package api
11 |
--------------------------------------------------------------------------------
/api/rest/decl.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "io"
5 | "net/http"
6 |
7 | "github.com/julienschmidt/httprouter"
8 | "github.com/ottemo/commerce/api"
9 | "github.com/ottemo/commerce/env"
10 | )
11 |
12 | // Package global constants
13 | const (
14 | ConstDebugLogStorage = "rest.log" // log storage for debug log records
15 |
16 | ConstErrorModule = "api/rest"
17 | ConstErrorLevel = env.ConstErrorLevelService
18 |
19 | ConstConfigPathAPI = "api"
20 | ConstConfigPathAPILog = "api.log"
21 | ConstConfigPathAPILogEnable = "api.log.enable"
22 | ConstConfigPathAPILogExclude = "api.log.exclude"
23 | )
24 |
25 | // DefaultRestService is a default implementer of InterfaceRestService
26 | // declared in "github.com/ottemo/commerce/api" package
27 | type DefaultRestService struct {
28 | ListenOn string
29 | Router *httprouter.Router
30 | Handlers []string
31 | }
32 |
33 | // DefaultRestApplicationContext is a structure to hold API request related information
34 | type DefaultRestApplicationContext struct {
35 | ResponseWriter http.ResponseWriter
36 | Request *http.Request
37 | RequestParameters map[string]string
38 | RequestArguments map[string]string
39 | RequestContent interface{}
40 | RequestFiles map[string]io.Reader
41 |
42 | Session api.InterfaceSession
43 | ContextValues map[string]interface{}
44 | Result interface{}
45 | }
46 |
--------------------------------------------------------------------------------
/api/rest/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package rest is a default implementation of a RESTful server. That package provides "InterfaceRestService" functionality
5 | declared in "github.com/ottemo/commerce/api" package.
6 |
7 | Package stands on a top of "github.com/julienschmidt/httprouter" package.
8 |
9 | RESTful server is a HTTP protocol accessible service you can use to interact with application. Refer to "http://[url-base]/"
10 | URL fo a list of API function application build providing. ("http://localhost:300" by default). The default interaction
11 | protocol for API functions is "application/json" (but not limited to, some API returns raw data, others "plain text", some
12 | of them supports couple content types).
13 |
14 | Ottemo API calls are supplied with ApplicationContext abstraction (refer "InterfaceApplicationContext"), to transfer API
15 | call related "attributes" and "content". For REST server attributes are parameters specified in URL string whereas content
16 | is a HTTP Request content. Notice that URL arguments are both - REST resource required parameters and optional URL parameters.
17 |
18 | Example:
19 | endpoint: /category/:categoryID/products
20 | api call: http://localhost/category/5488485b49c43d4283000067/products?action=count,sku=~10
21 |
22 | So, the arguments provided to API handler function are:
23 | categoryID="5488485b49c43d4283000067", action="count", sku="~10" (with string values)
24 |
25 | Session specification addressed to "OTTEMOSESSION=[sessionID]" COOKIE value. Each request with unspecified session will
26 | be supplied with new one session. SessionID will be returned in mentioned COOKIE value.
27 | */
28 | package rest
29 |
--------------------------------------------------------------------------------
/api/session/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package session is a default implementation of a application Session manager. That package provides "InterfaceSessionService"
5 | functionality declared in "github.com/ottemo/commerce/api" package.
6 |
7 | Sessions are API call related storage. So, each call to API function supplied with own separated storage which can holds
8 | a values related to that particular action. By default sessions have a lifetime, within that period application routines
9 | can hold information fo future usage for either themselves or other API calls. In order to use previously created session
10 | API call should specify sessionID within application context.
11 | */
12 | package session
13 |
--------------------------------------------------------------------------------
/api/session/service_memory.go:
--------------------------------------------------------------------------------
1 | // +build memsession
2 |
3 | // "service_memory.go" is a memory session storage - "memsession" build tag should be specified in order to use it
4 | // (session instances holds only on memory without flushing to longer term storage)
5 |
6 | package session
7 |
8 | import (
9 | "github.com/ottemo/commerce/api"
10 | )
11 |
12 | // init makes package self-initialization routine
13 | func init() {
14 | SessionService = InitDefaultSessionService()
15 |
16 | // service registration within system
17 | api.RegisterSessionService(SessionService)
18 | }
19 |
--------------------------------------------------------------------------------
/api/session/session.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | // InterfaceSession implementation
4 | // -------------------------------
5 |
6 | // GetID returns current session id
7 | func (it DefaultSession) GetID() string {
8 | return string(it)
9 | }
10 |
11 | // Get returns session value by a given key or nil - if not set
12 | func (it DefaultSession) Get(key string) interface{} {
13 | return SessionService.GetKey(string(it), key)
14 | }
15 |
16 | // Set assigns value to session key
17 | func (it DefaultSession) Set(key string, value interface{}) {
18 | SessionService.SetKey(string(it), key, value)
19 | }
20 |
21 | // IsEmpty checks if session contains data
22 | func (it DefaultSession) IsEmpty() bool {
23 | return SessionService.IsEmpty(it.GetID())
24 | }
25 |
26 | // Touch updates session last modification time to current moment
27 | func (it DefaultSession) Touch() error {
28 | return SessionService.Touch(string(it))
29 | }
30 |
31 | // Close makes current session instance expired
32 | func (it DefaultSession) Close() error {
33 | return SessionService.Close(string(it))
34 | }
35 |
--------------------------------------------------------------------------------
/api/session/session_test.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/utils"
6 | "math/rand"
7 | "testing"
8 | )
9 |
10 | // TestSessionsConcurrency tests synchronisation mechanisms between go routines
11 | func TestSessionsConcurrency(t *testing.T) {
12 | var sessions []api.InterfaceSession
13 |
14 | const sessionsNumber = 100
15 | const routinesNumber = 1000
16 |
17 | sync := make(chan bool)
18 |
19 | // preparing set of sessions to work with
20 | for i := 0; i < sessionsNumber; i++ {
21 | session, err := api.NewSession()
22 | if err != nil {
23 | t.Error(err)
24 | }
25 | sessions = append(sessions, session)
26 | }
27 |
28 | // making go-routines storm
29 | for i := 0; i < routinesNumber; i++ {
30 | go func() {
31 | session := sessions[rand.Intn(sessionsNumber)]
32 | for i := 0; i < 100; i++ {
33 | key := utils.InterfaceToString(i)
34 | session.Set(key, utils.InterfaceToInt(session.Get(key))+1)
35 | }
36 |
37 | if err := SessionService.GC(); err != nil {
38 | t.Error(err)
39 | }
40 |
41 | sync <- true
42 | }()
43 | }
44 |
45 | // waiting till all routines finishes their stuff
46 | finished := 0
47 | for finished = 0; finished < routinesNumber; <-sync {
48 | finished++
49 | }
50 |
51 | // closing all the sessions
52 | for i := 0; i < sessionsNumber; i++ {
53 | if err := sessions[i].Close(); err != nil {
54 | t.Error(err)
55 | }
56 | }
57 | }
58 |
59 | // BenchmarkIoOperations evaluates the performance of Set/Get operations
60 | func BenchmarkIoOperations(b *testing.B) {
61 | session, err := api.NewSession()
62 | if err != nil {
63 | b.Fail()
64 | }
65 |
66 | for i := 0; i < 100; i++ {
67 | session.Set("test", i)
68 |
69 | if result := session.Get("test"); result != i {
70 | b.Error("assigned value not matches:", result, "!=", i)
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/actors/blog/init.go:
--------------------------------------------------------------------------------
1 | package blog
2 |
3 | import (
4 | // self-initiabilizable sub-package
5 | _ "github.com/ottemo/commerce/app/actors/blog/post"
6 | )
7 |
--------------------------------------------------------------------------------
/app/actors/blog/post/decl.go:
--------------------------------------------------------------------------------
1 | // Package post is a default implementation of blog post related interfaces declared in
2 | // "github.com/ottemo/commerce/app/models/blog/post" package
3 | package post
4 |
5 | import (
6 | "time"
7 |
8 | "github.com/ottemo/commerce/env"
9 | )
10 |
11 | // Package global constants
12 | const (
13 | ConstBlogPostCollectionName = "blog_post"
14 |
15 | ConstErrorModule = "blog"
16 | ConstErrorLevel = env.ConstErrorLevelActor
17 | )
18 |
19 | // DefaultBlogPost is a default implementer of InterfaceBlogPost
20 | type DefaultBlogPost struct {
21 | id string
22 |
23 | identifier string
24 |
25 | title string
26 | excerpt string
27 | content string
28 |
29 | tags []interface{}
30 | featuredImage string
31 |
32 | createdAt time.Time
33 | updatedAt time.Time
34 | published bool
35 | }
36 |
--------------------------------------------------------------------------------
/app/actors/blog/post/init.go:
--------------------------------------------------------------------------------
1 | package post
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 |
8 | "github.com/ottemo/commerce/app/models"
9 | "github.com/ottemo/commerce/app/models/blog/post"
10 | )
11 |
12 | // init makes package self-initialization routine
13 | func init() {
14 | blogPostInstance := new(DefaultBlogPost)
15 | var _ post.InterfaceBlogPost = blogPostInstance
16 | if err := models.RegisterModel(post.ConstModelNameBlogPost, blogPostInstance); err != nil {
17 | _ = env.ErrorDispatch(err)
18 | }
19 |
20 | db.RegisterOnDatabaseStart(setupDB)
21 | api.RegisterOnRestServiceStart(setupAPI)
22 | }
23 |
24 | // setupDB prepares system database for package usage
25 | func setupDB() error {
26 | var err error
27 |
28 | collection, err := db.GetCollection(ConstBlogPostCollectionName)
29 | if err != nil {
30 | return env.ErrorDispatch(err)
31 | }
32 |
33 | if err = collection.AddColumn("identifier", db.ConstTypeVarchar, true); err != nil {
34 | return env.ErrorDispatch(err)
35 | }
36 | if err = collection.AddColumn("published", db.ConstTypeBoolean, true); err != nil {
37 | return env.ErrorDispatch(err)
38 | }
39 | if err = collection.AddColumn("title", db.ConstTypeVarchar, false); err != nil {
40 | return env.ErrorDispatch(err)
41 | }
42 | if err = collection.AddColumn("excerpt", db.ConstTypeText, false); err != nil {
43 | return env.ErrorDispatch(err)
44 | }
45 | if err = collection.AddColumn("content", db.ConstTypeText, false); err != nil {
46 | return env.ErrorDispatch(err)
47 | }
48 | if err = collection.AddColumn("created_at", db.ConstTypeDatetime, false); err != nil {
49 | return env.ErrorDispatch(err)
50 | }
51 | if err = collection.AddColumn("updated_at", db.ConstTypeDatetime, false); err != nil {
52 | return env.ErrorDispatch(err)
53 | }
54 | if err = collection.AddColumn("tags", "[]"+db.ConstTypeText, false); err != nil {
55 | return env.ErrorDispatch(err)
56 | }
57 | if err = collection.AddColumn("featured_image", db.ConstTypeVarchar, false); err != nil {
58 | return env.ErrorDispatch(err)
59 | }
60 |
61 | return nil
62 | }
63 |
--------------------------------------------------------------------------------
/app/actors/cart/config.go:
--------------------------------------------------------------------------------
1 | package cart
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | func setupConfig() error {
8 | config := env.GetConfig()
9 | if config == nil {
10 | err := env.ErrorNew(ConstErrorModule, env.ConstErrorLevelStartStop, "fed0dee4-3409-4533-a445-998d2290569a", "Cart module is unable to obtain configuration settings, using nil instead")
11 | return env.ErrorDispatch(err)
12 | }
13 |
14 | err := config.RegisterItem(env.StructConfigItem{
15 | Path: ConstConfigPathCartAbandonEmailSendTime,
16 | Value: "0",
17 | Type: env.ConstConfigTypeVarchar,
18 | Editor: "select",
19 | Options: map[string]string{
20 | "0": "Never",
21 | "-24": "After 24 hours",
22 | },
23 | Label: "Abandoned Cart Email - Send Time",
24 | Description: "If the customer abandons checkout, send them an email to complete their order.",
25 | Image: "",
26 | }, nil)
27 |
28 | if err != nil {
29 | return env.ErrorDispatch(err)
30 | }
31 |
32 | err = config.RegisterItem(env.StructConfigItem{
33 | Path: ConstConfigPathCartAbandonEmailTemplate,
34 | Value: "",
35 | Type: env.ConstConfigTypeHTML,
36 | Editor: "multiline_text",
37 | Options: "",
38 | Label: "Abandoned Cart Email - Template",
39 | Description: "",
40 | Image: "",
41 | }, nil)
42 |
43 | if err != nil {
44 | return env.ErrorDispatch(err)
45 | }
46 |
47 | return nil
48 | }
49 |
--------------------------------------------------------------------------------
/app/actors/cart/i_model.go:
--------------------------------------------------------------------------------
1 | package cart
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/cart"
6 | )
7 |
8 | // GetModelName returns model name we have implementation for
9 | func (it *DefaultCart) GetModelName() string {
10 | return cart.ConstCartModelName
11 | }
12 |
13 | // GetImplementationName returns name of current model implementation
14 | func (it *DefaultCart) GetImplementationName() string {
15 | return "DefaultCart"
16 | }
17 |
18 | // New makes new instance of model
19 | func (it *DefaultCart) New() (models.InterfaceModel, error) {
20 | return &DefaultCart{
21 | Items: make(map[int]cart.InterfaceCartItem),
22 | Info: make(map[string]interface{}),
23 | CustomInfo: make(map[string]interface{}),
24 | }, nil
25 | }
26 |
--------------------------------------------------------------------------------
/app/actors/category/category.i_listable.go:
--------------------------------------------------------------------------------
1 | package category
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/category"
6 | )
7 |
8 | // GetCollection returns collection of current instance type
9 | func (it *DefaultCategory) GetCollection() models.InterfaceCollection {
10 | model, _ := models.GetModel(category.ConstModelNameCategoryCollection)
11 | if result, ok := model.(category.InterfaceCategoryCollection); ok {
12 | return result
13 | }
14 |
15 | return nil
16 | }
17 |
--------------------------------------------------------------------------------
/app/actors/category/category.i_model.go:
--------------------------------------------------------------------------------
1 | package category
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/category"
6 | )
7 |
8 | // GetModelName returns model name
9 | func (it *DefaultCategory) GetModelName() string {
10 | return category.ConstModelNameCategory
11 | }
12 |
13 | // GetImplementationName returns model implementation name
14 | func (it *DefaultCategory) GetImplementationName() string {
15 | return "Default" + category.ConstModelNameCategory
16 | }
17 |
18 | // New returns new instance of model implementation object
19 | func (it *DefaultCategory) New() (models.InterfaceModel, error) {
20 | return &DefaultCategory{ProductIds: make([]string, 0)}, nil
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/category/collection.i_category_collection.go:
--------------------------------------------------------------------------------
1 | package category
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models/category"
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // GetDBCollection returns database collection
10 | func (it *DefaultCategoryCollection) GetDBCollection() db.InterfaceDBCollection {
11 | return it.listCollection
12 | }
13 |
14 | // ListCategories returns list of category model items
15 | func (it *DefaultCategoryCollection) ListCategories() []category.InterfaceCategory {
16 | var result []category.InterfaceCategory
17 |
18 | dbRecords, err := it.listCollection.Load()
19 | if err != nil {
20 | return result
21 | }
22 |
23 | for _, recordData := range dbRecords {
24 | categoryModel, err := category.GetCategoryModel()
25 | if err != nil {
26 | return result
27 | }
28 | if err := categoryModel.FromHashMap(recordData); err != nil {
29 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "f5cc61ae-cdff-4f38-9a72-12e235e586a5", err.Error())
30 | }
31 |
32 | result = append(result, categoryModel)
33 | }
34 |
35 | return result
36 | }
37 |
--------------------------------------------------------------------------------
/app/actors/category/collection.i_model.go:
--------------------------------------------------------------------------------
1 | package category
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/category"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns model name
11 | func (it *DefaultCategoryCollection) GetModelName() string {
12 | return category.ConstModelNameCategory
13 | }
14 |
15 | // GetImplementationName returns model implementation name
16 | func (it *DefaultCategoryCollection) GetImplementationName() string {
17 | return "Default" + category.ConstModelNameCategory
18 | }
19 |
20 | // New returns new instance of model implementation object
21 | func (it *DefaultCategoryCollection) New() (models.InterfaceModel, error) {
22 | dbCollection, err := db.GetCollection(ConstCollectionNameCategory)
23 | if err != nil {
24 | return nil, env.ErrorDispatch(err)
25 | }
26 |
27 | return &DefaultCategoryCollection{listCollection: dbCollection, listExtraAtributes: make([]string, 0)}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/category/decl.go:
--------------------------------------------------------------------------------
1 | // Package category is a default implementation of interfaces declared in
2 | // "github.com/ottemo/commerce/app/models/category" package
3 | package category
4 |
5 | import (
6 | "github.com/ottemo/commerce/app/models/category"
7 | "github.com/ottemo/commerce/db"
8 | "github.com/ottemo/commerce/env"
9 | )
10 |
11 | // Package global constants
12 | const (
13 | ConstCollectionNameCategory = "category"
14 | ConstCollectionNameCategoryProductJunction = "category_product"
15 |
16 | ConstErrorModule = "category"
17 | ConstErrorLevel = env.ConstErrorLevelActor
18 |
19 | ConstCategoryMediaTypeImage = "image"
20 | )
21 |
22 | // DefaultCategory is a default implementer of InterfaceCategory
23 | type DefaultCategory struct {
24 | id string
25 |
26 | Enabled bool
27 | Name string
28 | Description string
29 | Image string
30 | Parent category.InterfaceCategory
31 | Path string
32 | ProductIds []string
33 | }
34 |
35 | // DefaultCategoryCollection is a default implementer of InterfaceCategoryCollection
36 | type DefaultCategoryCollection struct {
37 | listCollection db.InterfaceDBCollection
38 | listExtraAtributes []string
39 | }
40 |
--------------------------------------------------------------------------------
/app/actors/checkout/decl.go:
--------------------------------------------------------------------------------
1 | // Package checkout is a default implementation of interfaces declared in
2 | // "github.com/ottemo/commerce/app/models/checkout" package
3 | package checkout
4 |
5 | import (
6 | "github.com/ottemo/commerce/app/models/cart"
7 | "github.com/ottemo/commerce/app/models/checkout"
8 | "github.com/ottemo/commerce/env"
9 | )
10 |
11 | // Package global constants
12 | const (
13 | ConstErrorModule = "checkout"
14 | ConstErrorLevel = env.ConstErrorLevelActor
15 | )
16 |
17 | // DefaultCheckout is a default implementer of InterfaceCheckout
18 | type DefaultCheckout struct {
19 | CartID string
20 | VisitorID string
21 | OrderID string
22 |
23 | SessionID string
24 |
25 | ShippingAddress map[string]interface{}
26 | BillingAddress map[string]interface{}
27 |
28 | PaymentMethodCode string
29 | ShippingMethodCode string
30 |
31 | ShippingRate checkout.StructShippingRate
32 |
33 | priceAdjustments []checkout.StructPriceAdjustment
34 |
35 | // should store details about applied adjustments for specific keys
36 | // 0 - cart, 1,2,3, .. n - index of cart item
37 | calculationDetailTotals map[int]map[string]float64
38 | cart cart.InterfaceCart
39 |
40 | Info map[string]interface{}
41 |
42 | calculateAmount float64
43 |
44 | // flags enables and disables during calculation to prevent recursion
45 | calculateFlag bool
46 | }
47 |
--------------------------------------------------------------------------------
/app/actors/checkout/i_model.go:
--------------------------------------------------------------------------------
1 | package checkout
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/checkout"
6 | )
7 |
8 | // GetModelName returns model name we have implementation for
9 | func (it *DefaultCheckout) GetModelName() string {
10 | return checkout.ConstCheckoutModelName
11 | }
12 |
13 | // GetImplementationName returns name of current model implementation
14 | func (it *DefaultCheckout) GetImplementationName() string {
15 | return "Default" + checkout.ConstCheckoutModelName
16 | }
17 |
18 | // New makes new instance of model
19 | func (it *DefaultCheckout) New() (models.InterfaceModel, error) {
20 | return &DefaultCheckout{Info: make(map[string]interface{})}, nil
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/checkout/init.go:
--------------------------------------------------------------------------------
1 | package checkout
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/env"
6 |
7 | "github.com/ottemo/commerce/app/models"
8 | "github.com/ottemo/commerce/app/models/checkout"
9 | )
10 |
11 | // init makes package self-initialization routine
12 | func init() {
13 | instance := new(DefaultCheckout)
14 | var _ checkout.InterfaceCheckout = instance
15 | if err := models.RegisterModel(checkout.ConstCheckoutModelName, instance); err != nil {
16 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "6dcce70e-7d29-46fb-a91a-6dc6d2836695", err.Error())
17 | }
18 |
19 | api.RegisterOnRestServiceStart(setupAPI)
20 | env.RegisterOnConfigStart(setupConfig)
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/cms/block/block.i_listable.go:
--------------------------------------------------------------------------------
1 | package block
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/cms"
6 | )
7 |
8 | // GetCollection returns collection of current instance type
9 | func (it *DefaultCMSBlock) GetCollection() models.InterfaceCollection {
10 | model, _ := models.GetModel(cms.ConstModelNameCMSBlockCollection)
11 | if result, ok := model.(cms.InterfaceCMSBlockCollection); ok {
12 | return result
13 | }
14 |
15 | return nil
16 | }
17 |
--------------------------------------------------------------------------------
/app/actors/cms/block/block.i_model.go:
--------------------------------------------------------------------------------
1 | package block
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/cms"
6 | )
7 |
8 | // GetModelName returns model name
9 | func (it *DefaultCMSBlock) GetModelName() string {
10 | return cms.ConstModelNameCMSBlock
11 | }
12 |
13 | // GetImplementationName returns model implementation name
14 | func (it *DefaultCMSBlock) GetImplementationName() string {
15 | return "DefaultCMSBlock"
16 | }
17 |
18 | // New returns new instance of model implementation object
19 | func (it *DefaultCMSBlock) New() (models.InterfaceModel, error) {
20 | return &DefaultCMSBlock{}, nil
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/cms/block/collection.i_cms_block_collection.go:
--------------------------------------------------------------------------------
1 | package block
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models/cms"
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // GetDBCollection returns database collection
10 | func (it *DefaultCMSBlockCollection) GetDBCollection() db.InterfaceDBCollection {
11 | return it.listCollection
12 | }
13 |
14 | // ListCMSBlocks returns list of cms block model items
15 | func (it *DefaultCMSBlockCollection) ListCMSBlocks() []cms.InterfaceCMSBlock {
16 | var result []cms.InterfaceCMSBlock
17 |
18 | dbRecords, err := it.listCollection.Load()
19 | if err != nil {
20 | return result
21 | }
22 |
23 | for _, recordData := range dbRecords {
24 | cmsBlockModel, err := cms.GetCMSBlockModel()
25 | if err != nil {
26 | return result
27 | }
28 | if err := cmsBlockModel.FromHashMap(recordData); err != nil {
29 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "143f447e-4bb7-46e5-9481-523ccf48fc70", err.Error())
30 | }
31 |
32 | result = append(result, cmsBlockModel)
33 | }
34 |
35 | return result
36 | }
37 |
--------------------------------------------------------------------------------
/app/actors/cms/block/collection.i_model.go:
--------------------------------------------------------------------------------
1 | package block
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/cms"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns model name
11 | func (it *DefaultCMSBlockCollection) GetModelName() string {
12 | return cms.ConstModelNameCMSBlockCollection
13 | }
14 |
15 | // GetImplementationName returns model implementation name
16 | func (it *DefaultCMSBlockCollection) GetImplementationName() string {
17 | return "Default" + cms.ConstModelNameCMSBlockCollection
18 | }
19 |
20 | // New returns new instance of model implementation object
21 | func (it *DefaultCMSBlockCollection) New() (models.InterfaceModel, error) {
22 | dbCollection, err := db.GetCollection(ConstCmsBlockCollectionName)
23 | if err != nil {
24 | return nil, env.ErrorDispatch(err)
25 | }
26 |
27 | return &DefaultCMSBlockCollection{listCollection: dbCollection, listExtraAtributes: make([]string, 0)}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/cms/block/decl.go:
--------------------------------------------------------------------------------
1 | // Package block is a default implementation of cms block related interfaces declared in
2 | // "github.com/ottemo/commerce/app/models/csm" package
3 | package block
4 |
5 | import (
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | "time"
9 | )
10 |
11 | // Package global constants
12 | const (
13 | ConstCmsBlockCollectionName = "cms_block"
14 |
15 | ConstErrorModule = "cms/block"
16 | ConstErrorLevel = env.ConstErrorLevelActor
17 | )
18 |
19 | // DefaultCMSBlock is a default implementer of InterfaceCMSBlock
20 | type DefaultCMSBlock struct {
21 | id string
22 |
23 | Identifier string
24 | Content string
25 |
26 | CreatedAt time.Time
27 | UpdatedAt time.Time
28 | }
29 |
30 | // DefaultCMSBlockCollection is a default implementer of InterfaceCMSBlockCollection
31 | type DefaultCMSBlockCollection struct {
32 | listCollection db.InterfaceDBCollection
33 | listExtraAtributes []string
34 | }
35 |
--------------------------------------------------------------------------------
/app/actors/cms/init.go:
--------------------------------------------------------------------------------
1 | // Package cms is just a grouping container for sub-packages auto init
2 | package cms
3 |
4 | import (
5 | // self-initiabilizable sub-package
6 | _ "github.com/ottemo/commerce/app/actors/cms/block"
7 |
8 | // self-initiabilizable sub-package
9 | _ "github.com/ottemo/commerce/app/actors/cms/page"
10 |
11 | // self-initiabilizable sub-package
12 | _ "github.com/ottemo/commerce/app/actors/cms/media"
13 | )
14 |
--------------------------------------------------------------------------------
/app/actors/cms/media/decl.go:
--------------------------------------------------------------------------------
1 | // Package media is a default implementation of cms page related interfaces declared in
2 | // "github.com/ottemo/commerce/app/models/cms" package
3 | package media
4 |
5 | import (
6 | "github.com/ottemo/commerce/env"
7 | "github.com/ottemo/commerce/media"
8 | )
9 |
10 | // Package global constants
11 | const (
12 | ConstErrorModule = "cms/media"
13 | ConstErrorLevel = env.ConstErrorLevelActor
14 |
15 | ConstStorageModel = "cms"
16 | ConstStorageObject = "media"
17 | )
18 |
19 | var (
20 | mediaStorage media.InterfaceMediaStorage
21 | )
22 |
--------------------------------------------------------------------------------
/app/actors/cms/media/helpers.go:
--------------------------------------------------------------------------------
1 | package media
2 |
3 | import (
4 | "github.com/ottemo/commerce/media"
5 | )
6 |
7 | // correctMediaType returns only supported media type according to srcMediaType specified
8 | func correctMediaType(srcMediaType string) string {
9 | var mediaType = srcMediaType
10 |
11 | if len(srcMediaType) == 0 {
12 | mediaType = media.ConstMediaTypeImage
13 | } else if mediaType != media.ConstMediaTypeImage {
14 | mediaType = media.ConstMediaTypeDocument
15 | }
16 |
17 | return mediaType
18 | }
19 |
--------------------------------------------------------------------------------
/app/actors/cms/media/init.go:
--------------------------------------------------------------------------------
1 | package media
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/app"
6 | "github.com/ottemo/commerce/env"
7 | "github.com/ottemo/commerce/media"
8 | "github.com/ottemo/commerce/utils"
9 | )
10 |
11 | // init makes package self-initialization routine
12 | func init() {
13 | api.RegisterOnRestServiceStart(setupAPI)
14 | app.OnAppStart(onAppStart)
15 |
16 | if err := utils.RegisterTemplateFunction("media", mediaTemplateDirective); err != nil {
17 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "706a59bb-cfdd-4e26-b8f1-42444daa3170", err.Error())
18 | }
19 | }
20 |
21 | func onAppStart() error {
22 | mediaStorageInstance, err := media.GetMediaStorage()
23 | if err != nil {
24 | return env.ErrorDispatch(err)
25 | }
26 |
27 | mediaStorage = mediaStorageInstance
28 | return nil
29 | }
30 |
31 | // mediaTemplateDirective - for adding image to pages
32 | // - use {{media "mediaName" .}} to fetch image URL
33 | // - Currently this method supports only images, but it is not used anywhere
34 | func mediaTemplateDirective(args ...interface{}) (string, error) {
35 | mediaName := ""
36 | if len(args) > 0 {
37 | mediaName = utils.InterfaceToString(args[0])
38 | }
39 | imagePath, err := mediaStorage.GetMediaPath(ConstStorageModel, ConstStorageObject, media.ConstMediaTypeImage)
40 | if err != nil {
41 | return "", env.ErrorDispatch(err)
42 | }
43 |
44 | commerceURL := app.GetcommerceURL(imagePath + mediaName)
45 |
46 | return "
", nil
47 | }
48 |
--------------------------------------------------------------------------------
/app/actors/cms/page/collection.i_cms_page_collection.go:
--------------------------------------------------------------------------------
1 | package page
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models/cms"
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // GetDBCollection returns database collection
10 | func (it *DefaultCMSPageCollection) GetDBCollection() db.InterfaceDBCollection {
11 | return it.listCollection
12 | }
13 |
14 | // ListCMSPages returns list of cms page model items
15 | func (it *DefaultCMSPageCollection) ListCMSPages() []cms.InterfaceCMSPage {
16 | var result []cms.InterfaceCMSPage
17 |
18 | dbRecords, err := it.listCollection.Load()
19 | if err != nil {
20 | return result
21 | }
22 |
23 | for _, recordData := range dbRecords {
24 | cmsPageModel, err := cms.GetCMSPageModel()
25 | if err != nil {
26 | return result
27 | }
28 | if err := cmsPageModel.FromHashMap(recordData); err != nil {
29 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "8e74b4b4-08e9-44d5-805d-e158f5af518c", err.Error())
30 | }
31 |
32 | result = append(result, cmsPageModel)
33 | }
34 |
35 | return result
36 | }
37 |
--------------------------------------------------------------------------------
/app/actors/cms/page/collection.i_model.go:
--------------------------------------------------------------------------------
1 | package page
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/cms"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns model name
11 | func (it *DefaultCMSPageCollection) GetModelName() string {
12 | return cms.ConstModelNameCMSPageCollection
13 | }
14 |
15 | // GetImplementationName returns model implementation name
16 | func (it *DefaultCMSPageCollection) GetImplementationName() string {
17 | return "Default" + cms.ConstModelNameCMSPageCollection
18 | }
19 |
20 | // New returns new instance of model implementation object
21 | func (it *DefaultCMSPageCollection) New() (models.InterfaceModel, error) {
22 | dbCollection, err := db.GetCollection(ConstCmsPageCollectionName)
23 | if err != nil {
24 | return nil, env.ErrorDispatch(err)
25 | }
26 |
27 | return &DefaultCMSPageCollection{listCollection: dbCollection, listExtraAtributes: make([]string, 0)}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/cms/page/decl.go:
--------------------------------------------------------------------------------
1 | // Package page is a default implementation of cms page related interfaces declared in
2 | // "github.com/ottemo/commerce/app/models/csm" package
3 | package page
4 |
5 | import (
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | "time"
9 | )
10 |
11 | // Package global constants
12 | const (
13 | ConstCmsPageCollectionName = "cms_page"
14 |
15 | ConstErrorModule = "cms/page"
16 | ConstErrorLevel = env.ConstErrorLevelActor
17 | )
18 |
19 | // DefaultCMSPage is a default implementer of InterfaceCMSPage
20 | type DefaultCMSPage struct {
21 | id string
22 |
23 | Enabled bool
24 | Identifier string
25 |
26 | Title string
27 | Content string
28 |
29 | CreatedAt time.Time
30 | UpdatedAt time.Time
31 | }
32 |
33 | // DefaultCMSPageCollection is a default implementer of InterfaceCMSPageCollection
34 | type DefaultCMSPageCollection struct {
35 | listCollection db.InterfaceDBCollection
36 | listExtraAtributes []string
37 | }
38 |
--------------------------------------------------------------------------------
/app/actors/cms/page/page.i_listable.go:
--------------------------------------------------------------------------------
1 | package page
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/cms"
6 | )
7 |
8 | // GetCollection returns collection of current instance type
9 | func (it *DefaultCMSPage) GetCollection() models.InterfaceCollection {
10 | model, _ := models.GetModel(cms.ConstModelNameCMSPageCollection)
11 | if result, ok := model.(cms.InterfaceCMSPageCollection); ok {
12 | return result
13 | }
14 |
15 | return nil
16 | }
17 |
--------------------------------------------------------------------------------
/app/actors/cms/page/page.i_model.go:
--------------------------------------------------------------------------------
1 | package page
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/cms"
6 | )
7 |
8 | // GetModelName returns model name
9 | func (it *DefaultCMSPage) GetModelName() string {
10 | return cms.ConstModelNameCMSPage
11 | }
12 |
13 | // GetImplementationName returns model implementation name
14 | func (it *DefaultCMSPage) GetImplementationName() string {
15 | return "DefaultCMSPage"
16 | }
17 |
18 | // New returns new instance of model implementation object
19 | func (it *DefaultCMSPage) New() (models.InterfaceModel, error) {
20 | return &DefaultCMSPage{}, nil
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/discount/coupon/config.go:
--------------------------------------------------------------------------------
1 | package coupon
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // setupConfig setups package configuration values for a system
8 | func setupConfig() error {
9 | config := env.GetConfig()
10 | if config == nil {
11 | err := env.ErrorNew(ConstErrorModule, env.ConstErrorLevelStartStop, "6a991234-a2bb-454e-9d5b-a7c2da1cdeb1", "can't obtain config")
12 | return env.ErrorDispatch(err)
13 | }
14 |
15 | err := config.RegisterItem(env.StructConfigItem{
16 | Path: ConstConfigPathDiscounts,
17 | Value: nil,
18 | Type: env.ConstConfigTypeGroup,
19 | Editor: "",
20 | Options: nil,
21 | Label: "Discounts",
22 | Description: "Discounts related options",
23 | Image: "",
24 | }, nil)
25 |
26 | if err != nil {
27 | return env.ErrorDispatch(err)
28 | }
29 |
30 | err = config.RegisterItem(env.StructConfigItem{
31 | Path: ConstConfigPathDiscountApplyPriority,
32 | Value: 2.10,
33 | Type: env.ConstConfigTypeFloat,
34 | Editor: "line_text",
35 | Options: nil,
36 | Label: "Discounts calculating position",
37 | Description: "This value used for using position to calculate it's possible applicable amount (Subtotal - 1, Shipping - 2, Grand total - 3)",
38 | Image: "",
39 | }, nil)
40 |
41 | if err != nil {
42 | return env.ErrorDispatch(err)
43 | }
44 |
45 | return nil
46 | }
47 |
--------------------------------------------------------------------------------
/app/actors/discount/coupon/decl.go:
--------------------------------------------------------------------------------
1 | // Package coupon is a default implementation of discount interface declared in
2 | // "github.com/ottemo/commerce/app/models/checkout" package
3 | package coupon
4 |
5 | import (
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // Package global constants
10 | const (
11 | ConstSessionKeyCurrentRedemptions = "current_redemption_codes"
12 | ConstCollectionNameCouponDiscounts = "coupon_discounts"
13 |
14 | ConstConfigPathDiscounts = "general.discounts"
15 | ConstConfigPathDiscountApplyPriority = "general.discounts.discount_apply_priority"
16 |
17 | ConstErrorModule = "coupon"
18 | ConstErrorLevel = env.ConstErrorLevelActor
19 | )
20 |
21 | // Coupon is a default implementer of InterfaceDiscount
22 | type Coupon struct{}
23 |
24 | // usedCoupons contains used coupon codes with visitorsId's, initialize from orders and updated on checkout success
25 | var usedCoupons map[string][]string
26 |
27 | type discount struct {
28 | Code string
29 | Name string
30 | Total float64
31 | Amount float64
32 | Percents float64
33 | Qty int
34 | }
35 |
--------------------------------------------------------------------------------
/app/actors/discount/giftcard/decl.go:
--------------------------------------------------------------------------------
1 | // Package giftcard creates and manage gift cards
2 | package giftcard
3 |
4 | import (
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // Package global constants
9 | const (
10 | ConstSessionKeyAppliedGiftCardCodes = "applied_giftcard_codes"
11 | ConstCollectionNameGiftCard = "gift_card"
12 |
13 | ConstConfigPathGiftEmailTemplate = "general.discounts.giftCard_email"
14 | ConstConfigPathGiftEmailSubject = "general.discounts.giftCard_email_subject"
15 | ConstConfigPathGiftCardSKU = "general.discounts.giftCard_SKU_code"
16 |
17 | ConstConfigPathGiftCardApplyPriority = "general.discounts.giftCard_apply_priority"
18 |
19 | ConstConfigPathGiftCardAdminBuyerName = "general.discounts.giftCard_admin_buyer_name"
20 | ConstConfigPathGiftCardAdminBuyerEmail = "general.discounts.giftCard_admin_buyer_email"
21 |
22 | ConstErrorModule = "giftcard"
23 | ConstErrorLevel = env.ConstErrorLevelActor
24 |
25 | ConstGiftCardStatusNew = "new"
26 | ConstGiftCardStatusApplied = "applied"
27 | ConstGiftCardStatusUsed = "used"
28 | ConstGiftCardStatusOverCredited = "negative"
29 | ConstGiftCardStatusRefilled = "refilled"
30 | ConstGiftCardStatusCancelled = "cancelled"
31 | ConstGiftCardStatusDelivered = "delivered"
32 | )
33 |
34 | // DefaultGiftcard is a default implementer of InterfaceDiscount
35 | type DefaultGiftcard struct{}
36 |
37 | // Shipping is a default free shipping rate for Gift Cards
38 | type Shipping struct{}
39 |
--------------------------------------------------------------------------------
/app/actors/discount/giftcard/i_shipping_method.go:
--------------------------------------------------------------------------------
1 | package giftcard
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/ottemo/commerce/app/models/checkout"
7 | "github.com/ottemo/commerce/env"
8 | "github.com/ottemo/commerce/utils"
9 | )
10 |
11 | // GetName returns name of shipping method
12 | func (it *Shipping) GetName() string {
13 | return "No Shipping"
14 | }
15 |
16 | // GetCode returns code of shipping method
17 | func (it *Shipping) GetCode() string {
18 | return "giftcards"
19 | }
20 |
21 | // IsAllowed checks for method applicability
22 | func (it *Shipping) IsAllowed(checkout checkout.InterfaceCheckout) bool {
23 | return true
24 | }
25 |
26 | // GetRates returns rates allowed by shipping method for a given checkout
27 | func (it *Shipping) GetRates(currentCheckout checkout.InterfaceCheckout) []checkout.StructShippingRate {
28 |
29 | result := []checkout.StructShippingRate{}
30 |
31 | giftCardSkuElement := utils.InterfaceToString(env.ConfigGetValue(ConstConfigPathGiftCardSKU))
32 |
33 | if cart := currentCheckout.GetCart(); cart != nil {
34 | for _, cartItem := range cart.GetItems() {
35 |
36 | cartProduct := cartItem.GetProduct()
37 | if cartProduct == nil {
38 | continue
39 | }
40 |
41 | if err := cartProduct.ApplyOptions(cartItem.GetOptions()); err != nil {
42 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "5fcd3354-17b5-43ad-946b-0146a6e0017a", err.Error())
43 | }
44 | if !strings.Contains(cartProduct.GetSku(), giftCardSkuElement) {
45 | return result
46 | }
47 | }
48 | }
49 |
50 | result = []checkout.StructShippingRate{
51 | checkout.StructShippingRate{
52 | Code: "freeshipping",
53 | Name: "GiftCards",
54 | Price: 0,
55 | }}
56 |
57 | return result
58 | }
59 |
60 | // GetAllRates returns all the shippmeng method rates available in the system.
61 | func (it Shipping) GetAllRates() []checkout.StructShippingRate {
62 |
63 | result := []checkout.StructShippingRate{
64 | checkout.StructShippingRate{
65 | Code: "freeshipping",
66 | Name: "GiftCards",
67 | Price: 0,
68 | },
69 | }
70 |
71 | return result
72 | }
73 |
--------------------------------------------------------------------------------
/app/actors/discount/saleprice/decl.go:
--------------------------------------------------------------------------------
1 | // Package saleprice is an implementation of discount interface declared in
2 | // "github.com/ottemo/commerce/app/models/checkout" package
3 | package saleprice
4 |
5 | import (
6 | "time"
7 |
8 | "github.com/ottemo/commerce/db"
9 | "github.com/ottemo/commerce/env"
10 |
11 | "github.com/ottemo/commerce/app/models"
12 | "github.com/ottemo/commerce/app/models/discount/saleprice"
13 | "github.com/ottemo/commerce/app/models/product"
14 | )
15 |
16 | // Package global constants
17 | const (
18 | ConstConfigPathGroup = "general.sale_price"
19 | ConstConfigPathEnabled = "general.sale_price.enabled"
20 | ConstConfigPathSalePriceApplyPriority = "general.sale_price.priority"
21 |
22 | ConstErrorModule = "saleprice"
23 | ConstErrorLevel = env.ConstErrorLevelActor
24 | )
25 |
26 | // DefaultSalePrice is an implementer of InterfaceDiscount
27 | type DefaultSalePrice struct {
28 | id string
29 |
30 | amount float64
31 | endDatetime time.Time
32 | productID string
33 | startDatetime time.Time
34 | }
35 |
36 | // DefaultSalePriceCollection is a default implementer of InterfaceSalePriceCollection
37 | type DefaultSalePriceCollection struct {
38 | listCollection db.InterfaceDBCollection
39 | listExtraAtributes []string
40 | }
41 |
42 | // SalePriceDelegate type implements InterfaceAttributesDelegate and have handles
43 | // on InterfaceStorable methods which should have call-back on model method call
44 | // in order to test it we are pushing the callback status to model instance
45 | type SalePriceDelegate struct {
46 | productInstance product.InterfaceProduct
47 | SalePrices []saleprice.InterfaceSalePrice
48 | }
49 |
50 | // salePriceDelegate variable that is currently used as a sale price delegate to extend product attributes
51 | var salePriceDelegate models.InterfaceAttributesDelegate
52 |
--------------------------------------------------------------------------------
/app/actors/order/item_collection.i_model.go:
--------------------------------------------------------------------------------
1 | package order
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/order"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns model name
11 | func (it *DefaultOrderItemCollection) GetModelName() string {
12 | return order.ConstModelNameOrderItemCollection
13 | }
14 |
15 | // GetImplementationName returns model implementation name
16 | func (it *DefaultOrderItemCollection) GetImplementationName() string {
17 | return "Default" + order.ConstModelNameOrderItemCollection
18 | }
19 |
20 | // New returns new instance of model implementation object
21 | func (it *DefaultOrderItemCollection) New() (models.InterfaceModel, error) {
22 | dbCollection, err := db.GetCollection(ConstCollectionNameOrderItems)
23 | if err != nil {
24 | return nil, env.ErrorDispatch(err)
25 | }
26 |
27 | return &DefaultOrderItemCollection{listCollection: dbCollection, listExtraAtributes: make([]string, 0)}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/order/item_collection.i_order_item_collection.go:
--------------------------------------------------------------------------------
1 | package order
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | )
6 |
7 | // GetDBCollection returns database collection
8 | func (it *DefaultOrderItemCollection) GetDBCollection() db.InterfaceDBCollection {
9 | return it.listCollection
10 | }
11 |
--------------------------------------------------------------------------------
/app/actors/order/order.i_listable.go:
--------------------------------------------------------------------------------
1 | package order
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/order"
6 | )
7 |
8 | // GetCollection returns collection of current instance type
9 | func (it *DefaultOrder) GetCollection() models.InterfaceCollection {
10 | model, _ := models.GetModel(order.ConstModelNameOrderCollection)
11 | if result, ok := model.(order.InterfaceOrderCollection); ok {
12 | return result
13 | }
14 |
15 | return nil
16 | }
17 |
--------------------------------------------------------------------------------
/app/actors/order/order.i_model.go:
--------------------------------------------------------------------------------
1 | package order
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/order"
6 | )
7 |
8 | // GetModelName returns model name we have implementation for
9 | func (it *DefaultOrder) GetModelName() string {
10 | return order.ConstModelNameOrder
11 | }
12 |
13 | // GetImplementationName returns name of current model implementation
14 | func (it *DefaultOrder) GetImplementationName() string {
15 | return "Default" + order.ConstModelNameOrder
16 | }
17 |
18 | // New makes new instance of model
19 | func (it *DefaultOrder) New() (models.InterfaceModel, error) {
20 | return &DefaultOrder{Items: make(map[int]order.InterfaceOrderItem)}, nil
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/order/order_collection.i_model.go:
--------------------------------------------------------------------------------
1 | package order
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/order"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns model name
11 | func (it *DefaultOrderCollection) GetModelName() string {
12 | return order.ConstModelNameOrderCollection
13 | }
14 |
15 | // GetImplementationName returns model implementation name
16 | func (it *DefaultOrderCollection) GetImplementationName() string {
17 | return "Default" + order.ConstModelNameOrderCollection
18 | }
19 |
20 | // New returns new instance of model implementation object
21 | func (it *DefaultOrderCollection) New() (models.InterfaceModel, error) {
22 | dbCollection, err := db.GetCollection(ConstCollectionNameOrder)
23 | if err != nil {
24 | return nil, env.ErrorDispatch(err)
25 | }
26 |
27 | return &DefaultOrderCollection{listCollection: dbCollection, listExtraAtributes: make([]string, 0)}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/order/order_collection.i_order_collection.go:
--------------------------------------------------------------------------------
1 | package order
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models/order"
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // GetDBCollection returns database collection
10 | func (it *DefaultOrderCollection) GetDBCollection() db.InterfaceDBCollection {
11 | return it.listCollection
12 | }
13 |
14 | // ListOrders returns array of products in model instance form
15 | func (it *DefaultOrderCollection) ListOrders() []order.InterfaceOrder {
16 | var result []order.InterfaceOrder
17 |
18 | dbRecords, err := it.listCollection.Load()
19 | if err != nil {
20 | return result
21 | }
22 |
23 | for _, dbRecordData := range dbRecords {
24 | orderModel, err := order.GetOrderModel()
25 | if err != nil {
26 | return result
27 | }
28 | if err := orderModel.FromHashMap(dbRecordData); err != nil {
29 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "29635564-a788-46d7-9485-f286ae481533", err.Error())
30 | }
31 |
32 | result = append(result, orderModel)
33 | }
34 |
35 | return result
36 | }
37 |
--------------------------------------------------------------------------------
/app/actors/other/emma/api.go:
--------------------------------------------------------------------------------
1 | package emma
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/env"
6 | "github.com/ottemo/commerce/utils"
7 | )
8 |
9 | // setupAPI setups package related API endpoint routines
10 | func setupAPI() error {
11 |
12 | service := api.GetRestService()
13 |
14 | // Public
15 | service.POST("emma/contact", APIEmmaAddContact)
16 |
17 | return nil
18 | }
19 |
20 | // APIEmmaAddContact - return message, after add contact
21 | // - email should be specified in "email" argument
22 | func APIEmmaAddContact(context api.InterfaceApplicationContext) (interface{}, error) {
23 |
24 | // check request context
25 | //---------------------
26 | requestData, err := api.GetRequestContentAsMap(context)
27 | if err != nil {
28 | return nil, env.ErrorDispatch(err)
29 | }
30 |
31 | if !utils.KeysInMapAndNotBlank(requestData, "email") {
32 | context.SetResponseStatusBadRequest()
33 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "6372b9a3-29f3-4ea4-a19f-40051a8f330b", "email has not been specified")
34 | }
35 | email := utils.InterfaceToString(requestData["email"])
36 |
37 | if !utils.KeysInMapAndNotBlank(requestData, "group_ids") {
38 | context.SetResponseStatusBadRequest()
39 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "eee81283-86c4-487c-a5b5-b78996be038e", "group_ids not specified")
40 | }
41 | groupIDs := utils.InterfaceToString(requestData["group_ids"])
42 |
43 | if !utils.ValidEmailAddress(email) {
44 | context.SetResponseStatusBadRequest()
45 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "b54b0917-acc0-469f-925e-8f85a1feac7b", "The email address, "+email+", is not in valid format.")
46 | }
47 |
48 | result, err := subscribe(email, groupIDs)
49 | if err != nil {
50 | context.SetResponseStatusInternalServerError()
51 | return nil, env.ErrorDispatch(err)
52 | }
53 |
54 | return result, nil
55 | }
56 |
--------------------------------------------------------------------------------
/app/actors/other/emma/decl.go:
--------------------------------------------------------------------------------
1 | package emma
2 |
3 | import "github.com/ottemo/commerce/env"
4 |
5 | // Package constants for Emma module
6 | const (
7 | ConstErrorModule = "emma"
8 | ConstErrorLevel = env.ConstErrorLevelAPI
9 |
10 | ConstConfigPathEmma = "general.emma"
11 | ConstConfigPathEmmaEnabled = "general.emma.enabled"
12 | ConstConfigPathEmmaPublicAPIKey = "general.emma.public_api_key"
13 | ConstConfigPathEmmaPrivateAPIKey = "general.emma.private_api_key"
14 | ConstConfigPathEmmaAccountID = "general.emma.account_id"
15 | ConstConfigPathEmmaSKU = "general.emma.trigger_sku"
16 | ConstConfigPathEmmaDefaultGroupIds = "general.emma.default_group_ids"
17 | )
18 |
19 | var (
20 | emmaService emmaServiceType
21 | )
22 |
23 | type emmaCredentialsGetter interface {
24 | get() (emmaCredentialsType, error)
25 | }
26 |
--------------------------------------------------------------------------------
/app/actors/other/emma/init.go:
--------------------------------------------------------------------------------
1 | package emma
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/app"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | func init() {
10 | app.OnAppStart(appStart)
11 | env.RegisterOnConfigStart(setupConfig)
12 | api.RegisterOnRestServiceStart(setupAPI)
13 | }
14 |
15 | func appStart() error {
16 | env.EventRegisterListener("checkout.success", checkoutSuccessHandler)
17 |
18 | emmaService = *newEmmaService()
19 |
20 | return nil
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/other/friendmail/config.go:
--------------------------------------------------------------------------------
1 | package friendmail
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // setupConfig setups package configuration values for a system
8 | func setupConfig() error {
9 | if config := env.GetConfig(); config != nil {
10 | err := config.RegisterItem(env.StructConfigItem{
11 | Path: ConstConfigPathFriendMail,
12 | Value: nil,
13 | Type: env.ConstConfigTypeGroup,
14 | Editor: "",
15 | Options: nil,
16 | Label: "Refer-A-Friend",
17 | Description: "Referal program",
18 | Image: "",
19 | }, nil)
20 |
21 | if err != nil {
22 | return env.ErrorDispatch(err)
23 | }
24 |
25 | err = config.RegisterItem(env.StructConfigItem{
26 | Path: ConstConfigPathFriendMailEmailSubject,
27 | Value: "Email friend",
28 | Type: env.ConstConfigTypeVarchar,
29 | Editor: "line_text",
30 | Options: nil,
31 | Label: "Email subject",
32 | Description: "Email subject for the friend form",
33 | Image: "",
34 | }, nil)
35 |
36 | if err != nil {
37 | return env.ErrorDispatch(err)
38 | }
39 |
40 | err = config.RegisterItem(env.StructConfigItem{
41 | Path: ConstConfigPathFriendMailEmailTemplate,
42 | Value: `Dear {{.friend_name}}
43 |
44 |
45 | Your friend sent you an email:
46 | {{.content}}`,
47 | Type: env.ConstConfigTypeHTML,
48 | Editor: "multiline_text",
49 | Options: nil,
50 | Label: "Email Body",
51 | Description: "Email body template for the friend form",
52 | Image: "",
53 | }, nil)
54 |
55 | if err != nil {
56 | return env.ErrorDispatch(err)
57 | }
58 | } else {
59 | err := env.ErrorNew(ConstErrorModule, env.ConstErrorLevelStartStop, "81e49a2f-906d-40ed-9a47-bb2b9c5e8f40", "Unable to obtain configuration for Friend Mail")
60 | return env.ErrorDispatch(err)
61 | }
62 |
63 | return nil
64 | }
65 |
--------------------------------------------------------------------------------
/app/actors/other/friendmail/decl.go:
--------------------------------------------------------------------------------
1 | // Package friendmail is an extension which provides ability to send email to friend
2 | package friendmail
3 |
4 | import (
5 | "github.com/dchest/captcha"
6 | "github.com/ottemo/commerce/env"
7 | "sync"
8 | "time"
9 | )
10 |
11 | // Package global constants
12 | const (
13 | ConstCollectionNameFriendMail = "friend_mail"
14 |
15 | ConstConfigPathFriendMail = "general.friendmail"
16 | ConstConfigPathFriendMailEmailTemplate = "general.friendmail.template"
17 | ConstConfigPathFriendMailEmailSubject = "general.friendmail.subject"
18 |
19 | ConstErrorModule = "friendmail"
20 | ConstErrorLevel = env.ConstErrorLevelActor
21 |
22 | ConstMaxCaptchaItems = 100000 // captcha maximum amount (to prevent memory leaks)
23 | ConstCaptchaLifeTime = 300 // seconds generated captcha works (5 min)
24 | )
25 |
26 | var (
27 | captchaValuesMutex sync.RWMutex // synchronization on captchaValues variable
28 | captchaValues map[string]time.Time // global variable to track generated captcha codes
29 | captchaStore captcha.Store
30 | )
31 |
--------------------------------------------------------------------------------
/app/actors/other/friendmail/init.go:
--------------------------------------------------------------------------------
1 | package friendmail
2 |
3 | import (
4 | "github.com/dchest/captcha"
5 | "github.com/ottemo/commerce/api"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | "time"
9 | )
10 |
11 | // init makes package self-initialization routine
12 | func init() {
13 | db.RegisterOnDatabaseStart(setupDB)
14 | env.RegisterOnConfigStart(setupConfig)
15 | api.RegisterOnRestServiceStart(setupAPI)
16 |
17 | captchaValues = make(map[string]time.Time)
18 |
19 | captchaStore = captcha.NewMemoryStore(ConstMaxCaptchaItems, time.Second*ConstCaptchaLifeTime)
20 | captcha.SetCustomStore(captchaStore)
21 |
22 | // starting timer for garbage collector
23 | if ConstCaptchaLifeTime > 0 {
24 | timerInterval := time.Second * ConstCaptchaLifeTime
25 | ticker := time.NewTicker(timerInterval)
26 | go func() {
27 | for _ = range ticker.C {
28 | captchaValuesMutex.Lock()
29 |
30 | currentTime := time.Now()
31 | for key, value := range captchaValues {
32 | if currentTime.Sub(value).Seconds() >= ConstCaptchaLifeTime {
33 | captchaStore.Get(key, true)
34 | delete(captchaValues, key)
35 | }
36 | }
37 |
38 | captchaValuesMutex.Unlock()
39 | }
40 | }()
41 | }
42 | }
43 |
44 | // setupDB prepares system database for package usage
45 | func setupDB() error {
46 |
47 | if collection, err := db.GetCollection(ConstCollectionNameFriendMail); err == nil {
48 | if err := collection.AddColumn("date", db.ConstTypeID, true); err != nil {
49 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "52acc483-0132-4252-acf3-9e965eefd73b", err.Error())
50 | }
51 | if err := collection.AddColumn("email", db.ConstTypeVarchar, true); err != nil {
52 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "53288f58-6cc8-4ae5-913d-d8f735a66678", err.Error())
53 | }
54 | if err := collection.AddColumn("data", db.ConstTypeJSON, false); err != nil {
55 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "c18bbf88-a104-4154-b8eb-0ccb6ae7a7e9", err.Error())
56 | }
57 | } else {
58 | return env.ErrorDispatch(err)
59 | }
60 |
61 | return nil
62 | }
63 |
--------------------------------------------------------------------------------
/app/actors/other/grouping/decl.go:
--------------------------------------------------------------------------------
1 | // Package grouping implements products set grouping into another set
2 | package grouping
3 |
4 | import (
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // Package global constants
9 | const (
10 | ConstErrorModule = "grouping"
11 | ConstErrorLevel = env.ConstErrorLevelActor
12 |
13 | ConstGroupingConfigPath = "general.stock.grouprules"
14 | )
15 |
16 | // Package global variables
17 | var (
18 | currentRules = make([]interface{}, 0)
19 | )
20 |
--------------------------------------------------------------------------------
/app/actors/other/grouping/init.go:
--------------------------------------------------------------------------------
1 | package grouping
2 |
3 | import (
4 | "github.com/ottemo/commerce/app"
5 | "github.com/ottemo/commerce/env"
6 | "github.com/ottemo/commerce/utils"
7 | )
8 |
9 | // init makes package self-initialization routine before app start
10 | func init() {
11 | app.OnAppStart(onAppStart)
12 | env.RegisterOnConfigStart(setupConfig)
13 | }
14 |
15 | // onAppStart makes module initialization on application startup
16 | func onAppStart() error {
17 |
18 | rules, err := utils.DecodeJSONToArray(env.ConfigGetValue(ConstGroupingConfigPath))
19 | if err != nil {
20 | rules = make([]interface{}, 0)
21 | }
22 | currentRules = rules
23 |
24 | env.EventRegisterListener("api.cart.update", updateCartHandler)
25 |
26 | return nil
27 | }
28 |
--------------------------------------------------------------------------------
/app/actors/other/mailchimp/decl.go:
--------------------------------------------------------------------------------
1 | package mailchimp
2 |
3 | import "github.com/ottemo/commerce/env"
4 |
5 | // Package constants for Mailchimp module
6 | const (
7 | ConstErrorModule = "mailchimp"
8 | ConstErrorLevel = env.ConstErrorLevelActor
9 |
10 | ConstMailchimpSubscribeStatus = "subscribed"
11 |
12 | ConstConfigPathMailchimp = "general.mailchimp"
13 | ConstConfigPathMailchimpEnabled = "general.mailchimp.enabled"
14 | ConstConfigPathMailchimpAPIKey = "general.mailchimp.api_key"
15 | ConstConfigPathMailchimpBaseURL = "general.mailchimp.base_url"
16 | ConstConfigPathMailchimpSupportAddress = "general.mailchimp.support_addr"
17 | ConstConfigPathMailchimpEmailTemplate = "general.mailchimp.template"
18 | ConstConfigPathMailchimpSubjectLine = "general.mailchimp.subject_line"
19 | ConstConfigPathMailchimpList = "general.mailchimp.subscribe_to_list"
20 | ConstConfigPathMailchimpSKU = "general.mailchimp.trigger_sku"
21 | )
22 |
23 | // Registration is a struct to hold a single registation for a Mailchimp mailing list.
24 | type Registration struct {
25 | EmailAddress string `json:"email_address"`
26 | Status string `json:"status"`
27 | MergeFields map[string]string `json:"merge_fields"`
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/other/mailchimp/init.go:
--------------------------------------------------------------------------------
1 | package mailchimp
2 |
3 | import (
4 | "github.com/ottemo/commerce/app"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | func init() {
9 | app.OnAppStart(appStart)
10 | env.RegisterOnConfigStart(setupConfig)
11 | }
12 |
13 | func appStart() error {
14 | env.EventRegisterListener("checkout.success", checkoutSuccessHandler)
15 |
16 | return nil
17 | }
18 |
--------------------------------------------------------------------------------
/app/actors/other/mailchimp/mailchimp_test.go:
--------------------------------------------------------------------------------
1 | package mailchimp_test
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "testing"
7 | "time"
8 |
9 | "github.com/ottemo/commerce/app/actors/other/mailchimp"
10 | "github.com/ottemo/commerce/env"
11 | "github.com/ottemo/commerce/test"
12 | "github.com/ottemo/commerce/db"
13 | )
14 |
15 | func TestMailchimpSubscribe(t *testing.T) {
16 | // start app
17 | err := test.StartAppInTestingMode()
18 | if err != nil {
19 | t.Error(err)
20 | }
21 |
22 | db.RegisterOnDatabaseStart(func () error {
23 | testMailchimpSubscribe(t)
24 | return nil
25 | })
26 | }
27 |
28 | func testMailchimpSubscribe(tst *testing.T) {
29 | //set the configuration to allow mailchimp
30 | var config = env.GetConfig()
31 | if err := config.SetValue(mailchimp.ConstConfigPathMailchimpEnabled, true); err != nil {
32 | tst.Error(err)
33 | }
34 | if err := config.SetValue(mailchimp.ConstConfigPathMailchimpAPIKey, "23dbf42618e8f43e624a6dd89de9bd46-us12"); err != nil {
35 | tst.Error(err)
36 | }
37 | if err := config.SetValue(mailchimp.ConstConfigPathMailchimpBaseURL, "https://us12.api.mailchimp.com/3.0/"); err != nil {
38 | tst.Error(err)
39 | }
40 |
41 | rand.Seed(time.Now().UTC().UnixNano())
42 | testRegistration := mailchimp.Registration{
43 | EmailAddress: fmt.Sprintf("test+%d@myottemotest.com", rand.Int()),
44 | Status: "subscribed",
45 | MergeFields: map[string]string{
46 | "FNAME": "Test",
47 | "LNAME": "User",
48 | },
49 | }
50 |
51 | if err := mailchimp.Subscribe("b9537d1e65", testRegistration); err != nil {
52 | tst.Error(err)
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/actors/other/shipstation/decl.go:
--------------------------------------------------------------------------------
1 | package shipstation
2 |
3 | import "github.com/ottemo/commerce/env"
4 |
5 | const (
6 | ConstErrorModule = "shipstation"
7 | ConstErrorLevel = env.ConstErrorLevel
8 | )
9 |
10 | // struct goes here
11 | type Orders struct {
12 | Orders []Order `xml:"Order"`
13 | }
14 |
15 | type Order struct {
16 | OrderId string `xml:"OrderID"`
17 | OrderNumber string
18 | OrderDate string // Set to string because we can't control the date formatting otherwise
19 | OrderStatus string
20 | LastModified string // Same formatting issue
21 | TaxAmount float64
22 | ShippingAmount float64
23 | OrderTotal float64
24 |
25 | // Containers
26 | Customer Customer
27 | Items []OrderItem `xml:"Items>Item"`
28 | }
29 |
30 | type Customer struct {
31 | CustomerCode string // We use email address here
32 |
33 | // Containers
34 | BillingAddress BillingAddress `xml:"BillTo"`
35 | ShippingAddress ShippingAddress `xml:"ShipTo"`
36 | }
37 |
38 | type BillingAddress struct {
39 | Name string
40 | }
41 |
42 | type ShippingAddress struct {
43 | Name string
44 | Address1 string
45 | City string
46 | State string
47 | PostalCode string
48 | Country string
49 | }
50 |
51 | type OrderItem struct {
52 | Sku string `xml:"SKU"`
53 | Name string
54 | Quantity int
55 | UnitPrice float64
56 | Adjustment bool
57 | }
58 |
--------------------------------------------------------------------------------
/app/actors/other/shipstation/init.go:
--------------------------------------------------------------------------------
1 | package shipstation
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | func init() {
9 | env.RegisterOnConfigStart(setupConfig)
10 | api.RegisterOnRestServiceStart(setupAPI)
11 | }
12 |
--------------------------------------------------------------------------------
/app/actors/other/shipstation/internal.go:
--------------------------------------------------------------------------------
1 | package shipstation
2 |
3 | import "github.com/ottemo/commerce/env"
4 |
5 | // buildTrackingUrl Assemble a tracking url from the carrier and tracking number
6 | // - http://verysimple.com/2011/07/06/ups-tracking-url/
7 | // - http://eshipguy.com/tracking/
8 | func buildTrackingUrl(carrier string, trackingNumber string) string {
9 | var trackingUrl string
10 |
11 | // These are the recognized carriers that shipstation might pass back
12 | trackingMap := map[string]string{
13 | "AccessWorldwide": "",
14 | "APC": "",
15 | "Asendia": "",
16 | "AustraliaPost": "",
17 | "BrokersWorldWide": "",
18 | "CanadaPost": "",
19 | "DHL": "http://track.dhl-usa.com/TrackByNbr.asp?ShipmentNumber=",
20 | "DHLCanada": "",
21 | "DHLGlobalMail": "",
22 | "FedEx": "http://www.fedex.com/Tracking?action=track&tracknumbers=",
23 | "FedExCanada": "",
24 | "FedExInternationalMailService": "",
25 | "FirstMile": "",
26 | "Globegistics": "",
27 | "IMEX": "",
28 | "LoneStar": "",
29 | "Newgistics": "",
30 | "OnTrac": "",
31 | "Other": "",
32 | "RoyalMail": "",
33 | "UPS": "http://wwwapps.ups.com/WebTracking/track?track=yes&trackNums=",
34 | "UPSMI": "https://www.ups-mi.net/packageID/packageid.aspx?pid=",
35 | "USPS": "https://tools.usps.com/go/TrackConfirmAction_input?qtc_tLabels1=",
36 | }
37 |
38 | trackingUrl = trackingMap[carrier]
39 | if trackingUrl == "" {
40 | env.LogError(env.ErrorNew(ConstErrorModule, ConstErrorLevel, "1799137F-0FAD-4200-BAFF-954C30FCE674", "We don't have a tracking url set up for a certain carrier: "+carrier))
41 | } else {
42 | trackingUrl += trackingNumber
43 | }
44 |
45 | return trackingUrl
46 | }
47 |
--------------------------------------------------------------------------------
/app/actors/other/trustpilot/decl.go:
--------------------------------------------------------------------------------
1 | // Package trustpilot implements trust pilot functions
2 | package trustpilot
3 |
4 | import (
5 | "time"
6 | )
7 |
8 | // Package global constants
9 | const (
10 | ConstEmailSubject = "Purchase feedback"
11 |
12 | ConstErrorModule = "trustpilot"
13 | ConstErrorLevel = 1 // if i tell you to log, then do it
14 |
15 | ConstOrderCustomInfoLinkKey = "trustpilot_link"
16 | ConstOrderCustomInfoSentKey = "trustpilot_sent"
17 |
18 | ConstConfigPathTrustPilot = "general.trustpilot"
19 | ConstConfigPathTrustPilotEnabled = "general.trustpilot.enabled"
20 | ConstConfigPathTrustPilotAPIKey = "general.trustpilot.apiKey"
21 | ConstConfigPathTrustPilotAPISecret = "general.trustpilot.apiSecret"
22 | ConstConfigPathTrustPilotBusinessUnitID = "general.trustpilot.businessUnitID"
23 | ConstConfigPathTrustPilotUsername = "general.trustpilot.username"
24 | ConstConfigPathTrustPilotPassword = "general.trustpilot.password"
25 | ConstConfigPathTrustPilotEmailTemplate = "general.trustpilot.emailTemplate"
26 | ConstConfigPathTrustPilotProductBrand = "general.trustpilot.productBrand"
27 |
28 | ConstRatingSummaryURL = "https://api.trustpilot.com/v1/private/product-reviews/business-units/{businessUnitId}/summaries"
29 | )
30 |
31 | // TODO: we should use some caching module instead of just global variables
32 | // Package global variables
33 | var (
34 | lastTimeSummariesUpdate time.Time
35 | summariesCache interface{}
36 | )
37 |
--------------------------------------------------------------------------------
/app/actors/other/vantagepoint/actors/decl.go:
--------------------------------------------------------------------------------
1 | package actors
2 |
3 | import "github.com/ottemo/commerce/env"
4 |
5 | const (
6 | ConstErrorModule = "vantagepoint"
7 | ConstErrorLevel = env.ConstErrorLevelActor
8 | )
9 |
10 |
--------------------------------------------------------------------------------
/app/actors/other/vantagepoint/decl.go:
--------------------------------------------------------------------------------
1 | package vantagepoint
2 |
3 | import "github.com/ottemo/commerce/env"
4 |
5 | const (
6 | ConstErrorModule = "vantagepoint"
7 | ConstErrorLevel = env.ConstErrorLevelActor
8 | ConstLogStorage = "vantagepoint.log"
9 |
10 | ConstConfigPathVantagePoint = "general.vantagepoint"
11 | ConstConfigPathVantagePointScheduleEnabled = "general.vantagepoint.schedule.enabled"
12 | ConstConfigPathVantagePointScheduleHour = "general.vantagepoint.schedule.hour"
13 | ConstConfigPathVantagePointUploadPath = "general.vantagepoint.upload.path"
14 | ConstConfigPathVantagePointUploadFileMask = "general.vantagepoint.upload.filemask"
15 |
16 | ConstSchedulerTaskName = "vantagePointCheckNewUploads"
17 | )
18 |
19 | type UploadProcessorInterface interface {
20 | Process() error
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/other/vantagepoint/init.go:
--------------------------------------------------------------------------------
1 | package vantagepoint
2 |
3 | import (
4 | "github.com/ottemo/commerce/app"
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | func init() {
10 | initHoursList()
11 |
12 | db.RegisterOnDatabaseStart(onDatabaseStart)
13 |
14 | env.RegisterOnConfigStart(setupConfig)
15 | }
16 |
17 | func onDatabaseStart() error {
18 | app.OnAppStart(onAppStart)
19 |
20 | return nil
21 | }
22 |
23 | func onAppStart() error {
24 | if err := scheduleCheckNewUploads(); err != nil {
25 | _ = env.ErrorDispatch(err)
26 | }
27 |
28 | return nil
29 | }
30 |
--------------------------------------------------------------------------------
/app/actors/payment/authorizenet/init.go:
--------------------------------------------------------------------------------
1 | package authorizenet
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/env"
6 |
7 | "github.com/ottemo/commerce/app/models/checkout"
8 | )
9 |
10 | // init makes package self-initialization routine
11 | func init() {
12 | if err := checkout.RegisterPaymentMethod(new(DirectPostMethod)); err != nil {
13 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "c36db383-87db-45be-9cab-ce5c0967686c", err.Error())
14 | }
15 | if err := checkout.RegisterPaymentMethod(new(RestMethod)); err != nil {
16 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "c7d65f90-7ba4-48b9-96b9-31e9d4cd49d0", err.Error())
17 | }
18 | api.RegisterOnRestServiceStart(setupAPI)
19 | env.RegisterOnConfigStart(setupConfig)
20 | }
21 |
--------------------------------------------------------------------------------
/app/actors/payment/braintree/decl.go:
--------------------------------------------------------------------------------
1 | // Package braintree is a "braintree payments" implementation of payment method interface declared in
2 | // "github.com/ottemo/commerce/app/models/checkout" package
3 | package braintree
4 |
5 | import (
6 | "github.com/lionelbarrow/braintree-go"
7 |
8 | "github.com/ottemo/commerce/env"
9 | )
10 |
11 | // Package global constants
12 | const (
13 | // --------------------------------------
14 | // Because of multiple payment modules supported by Braintree constant names and values are divided into
15 | // General - overall values
16 | // Method - specific per method values
17 | //
18 | // Note: group name is prefix of elements grouped in frontend
19 |
20 | // --------------------------------------
21 | // General
22 |
23 | ConstGeneralConfigPathGroup = "payment.braintreeGeneral"
24 |
25 | ConstGeneralConfigPathEnabled = "payment.braintreeGeneral.enabled"
26 | ConstGeneralMethodConfigPathName = "payment.braintreeGeneral.name" // User customized name of the payment method
27 |
28 | ConstGeneralConfigPathEnvironment = "payment.braintreeGeneral.environment"
29 | ConstGeneralConfigPathMerchantID = "payment.braintreeGeneral.merchantID"
30 | ConstGeneralConfigPathPublicKey = "payment.braintreeGeneral.publicKey"
31 | ConstGeneralConfigPathPrivateKey = "payment.braintreeGeneral.privateKey"
32 |
33 |
34 | ConstEnvironmentSandbox = string(braintree.Sandbox)
35 | ConstEnvironmentProduction = string(braintree.Production)
36 |
37 | ConstErrorModule = "payment/braintree"
38 | ConstErrorLevel = env.ConstErrorLevelActor
39 |
40 | constLogStorage = "braintree.log"
41 |
42 |
43 | constCCMethodCode = "braintree" // Method code used in business logic
44 | constCCMethodInternalName = "Braintree Credit Card" // Human readable name of payment method
45 |
46 | )
47 |
48 | // CreditCardMethod is a implementer of InterfacePaymentMethod for a Credit Card payment method
49 | type CreditCardMethod struct{}
50 |
--------------------------------------------------------------------------------
/app/actors/payment/braintree/init.go:
--------------------------------------------------------------------------------
1 | package braintree
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 |
6 | "github.com/ottemo/commerce/app/models/checkout"
7 | )
8 |
9 | // init makes package self-initialization routine
10 | func init() {
11 | if err := checkout.RegisterPaymentMethod(new(CreditCardMethod)); err != nil {
12 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "194b26f7-3399-4121-80f1-e24305708871", err.Error())
13 | }
14 | env.RegisterOnConfigStart(setupConfig)
15 | }
16 |
--------------------------------------------------------------------------------
/app/actors/payment/checkmo/decl.go:
--------------------------------------------------------------------------------
1 | // Package checkmo is a "Check Money Order" implementation of payment method interface declared in
2 | // "github.com/ottemo/commerce/app/models/checkout" package
3 | package checkmo
4 |
5 | import (
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // Package global constants
10 | const (
11 | ConstPaymentCode = "checkmo"
12 | ConstPaymentName = "Check/Money Order"
13 |
14 | ConstConfigPathGroup = "payment.checkmo"
15 | ConstConfigPathEnabled = "payment.checkmo.enabled"
16 | ConstConfigPathTitle = "payment.checkmo.title"
17 |
18 | ConstErrorModule = "payment/checkmo"
19 | ConstErrorLevel = env.ConstErrorLevelActor
20 | )
21 |
22 | // CheckMoneyOrder is a simplest implementer of InterfacePaymentMethod
23 | type CheckMoneyOrder struct{}
24 |
--------------------------------------------------------------------------------
/app/actors/payment/checkmo/init.go:
--------------------------------------------------------------------------------
1 | package checkmo
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 |
6 | "github.com/ottemo/commerce/app/models/checkout"
7 | )
8 |
9 | // init makes package self-initialization routine
10 | func init() {
11 | if err := checkout.RegisterPaymentMethod(new(CheckMoneyOrder)); err != nil {
12 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "aa6367b6-3bf2-4843-915b-be8fbe3ffa6a", err.Error())
13 | }
14 | env.RegisterOnConfigStart(setupConfig)
15 | }
16 |
--------------------------------------------------------------------------------
/app/actors/payment/paypal/init.go:
--------------------------------------------------------------------------------
1 | package paypal
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/env"
6 |
7 | "github.com/ottemo/commerce/app/models/checkout"
8 | )
9 |
10 | // init makes package self-initialization routine
11 | func init() {
12 | if err := checkout.RegisterPaymentMethod(new(Express)); err != nil {
13 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "4f72b52a-9af1-4725-a1dc-6731774be323", err.Error())
14 | }
15 | if err := checkout.RegisterPaymentMethod(new(PayFlowAPI)); err != nil {
16 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "4abc8432-31e5-4ef7-bcdc-362b4b32fa8a", err.Error())
17 | }
18 | api.RegisterOnRestServiceStart(setupAPI)
19 | env.RegisterOnConfigStart(setupConfig)
20 | }
21 |
--------------------------------------------------------------------------------
/app/actors/payment/paypal/internal.go:
--------------------------------------------------------------------------------
1 | package paypal
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | )
6 |
7 | // getCreditCardName returns credit card valid name
8 | func getCreditCardName(creditCardType string) string {
9 |
10 | switch creditCardType {
11 | case "0", "VI", "Visa", "VISA":
12 | return "VISA"
13 |
14 | case "1", "MC", "Master Card", "MasterCard":
15 | return "MasterCard"
16 |
17 | case "2", "DS", "Discover":
18 | return "Discover"
19 |
20 | case "3", "AX", "AE", "AmericanExpress", "American Express":
21 | return "AmericanExpress"
22 |
23 | case "4", "DC", "Diner’s Club", "Diner’sClub", "DinersClub":
24 | return "DinersClub"
25 |
26 | case "5", "JC", "JCB":
27 | return "JCB"
28 | }
29 |
30 | return creditCardType
31 | }
32 |
33 | // getCountryCode return valid country code from it's name
34 | func getCountryCode(country string) string {
35 |
36 | switch country {
37 | case "USA", "US", "United States", "UnitedStates":
38 | return "US"
39 |
40 | default:
41 | for code, name := range models.ConstCountriesList {
42 | if name == country {
43 | return code
44 | }
45 | }
46 | return "US"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/actors/payment/stripe/decl.go:
--------------------------------------------------------------------------------
1 | package stripe
2 |
3 | // Stripe package constants
4 | const (
5 | ConstPaymentCode = "stripe"
6 | ConstPaymentName = "Stripe"
7 |
8 | ConstConfigPathGroup = "payment.stripe"
9 | ConstConfigPathEnabled = "payment.stripe.enabled"
10 | ConstConfigPathName = "payment.stripe.name"
11 | ConstConfigPathAPIKey = "payment.stripe.apiKey"
12 |
13 | ConstErrorModule = "payment/stripe"
14 | )
15 |
16 | // Payment is the struct to hold the payment information for a visitor's order
17 | type Payment struct{}
18 |
--------------------------------------------------------------------------------
/app/actors/payment/stripe/init.go:
--------------------------------------------------------------------------------
1 | package stripe
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 |
6 | "github.com/ottemo/commerce/app/models/checkout"
7 | )
8 |
9 | func init() {
10 | if err := checkout.RegisterPaymentMethod(new(Payment)); err != nil {
11 | _ = env.ErrorNew(ConstErrorModule, env.ConstErrorLevelActor, "ea29fa2a-f947-4e7f-aff0-b0965256c751", err.Error())
12 | }
13 | env.RegisterOnConfigStart(setupConfig)
14 | }
15 |
--------------------------------------------------------------------------------
/app/actors/payment/zeropay/decl.go:
--------------------------------------------------------------------------------
1 | // Package zeropay is a "Zero Payment" implementation of payment method interface declared in
2 | // "github.com/ottemo/commerce/app/models/checkout" package
3 | package zeropay
4 |
5 | import (
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // Package global constants
10 | const (
11 | ConstPaymentZeroPaymentCode = "zeropay"
12 | ConstPaymentName = "Zero Pay"
13 |
14 | ConstConfigPathGroup = "payment.zeropay"
15 | ConstConfigPathEnabled = "payment.zeropay.enabled"
16 | ConstConfigPathName = "payment.zeropay.name"
17 |
18 | ConstErrorModule = "payment/zeropay"
19 | ConstErrorLevel = env.ConstErrorLevelActor
20 | )
21 |
22 | // ZeroAmountPayment is a implementer of InterfacePaymentMethod for zero amount payments
23 | type ZeroAmountPayment struct{}
24 |
--------------------------------------------------------------------------------
/app/actors/payment/zeropay/init.go:
--------------------------------------------------------------------------------
1 | package zeropay
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 |
6 | "github.com/ottemo/commerce/app/models/checkout"
7 | )
8 |
9 | // init makes package self-initialization routine
10 | func init() {
11 | if err := checkout.RegisterPaymentMethod(new(ZeroAmountPayment)); err != nil {
12 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "c2f3aaed-7a1e-4b43-bc31-40cdec749ac5", err.Error())
13 | }
14 | env.RegisterOnConfigStart(setupConfig)
15 | }
16 |
--------------------------------------------------------------------------------
/app/actors/product/decl.go:
--------------------------------------------------------------------------------
1 | // Package product is a implementation of interfaces declared in
2 | // "github.com/ottemo/commerce/app/models/product" package
3 | package product
4 |
5 | import (
6 | "github.com/ottemo/commerce/app/helpers/attributes"
7 | "github.com/ottemo/commerce/db"
8 | "github.com/ottemo/commerce/env"
9 | )
10 |
11 | // Package global constants
12 | const (
13 | ConstCollectionNameProduct = "product"
14 |
15 | ConstErrorModule = "product"
16 | ConstErrorLevel = env.ConstErrorLevelActor
17 |
18 | ConstProductMediaTypeImage = "image"
19 |
20 | ConstSwatchImageDefaultFormat = "jpeg"
21 | ConstSwatchImageDefaultExtention = "jpeg"
22 | )
23 |
24 | // DefaultProduct is a default implementer of InterfaceProduct
25 | type DefaultProduct struct {
26 | id string
27 |
28 | Enabled bool
29 |
30 | Sku string
31 | Name string
32 |
33 | ShortDescription string
34 | Description string
35 |
36 | DefaultImage string
37 |
38 | Price float64
39 |
40 | Weight float64
41 |
42 | Options map[string]interface{}
43 |
44 | RelatedProductIds []string
45 |
46 | Visible bool
47 |
48 | // appliedOptions tracks options were applied to current instance
49 | appliedOptions map[string]interface{}
50 |
51 | // updatedQty holds qty should be updated during save operation ("" item holds qty value)
52 | updatedQty []map[string]interface{}
53 |
54 | customAttributes *attributes.ModelCustomAttributes
55 | externalAttributes *attributes.ModelExternalAttributes
56 | }
57 |
58 | // DefaultProductCollection is a default implementer of InterfaceProduct
59 | type DefaultProductCollection struct {
60 | listCollection db.InterfaceDBCollection
61 | listExtraAtributes []string
62 | }
63 |
--------------------------------------------------------------------------------
/app/actors/product/review/decl.go:
--------------------------------------------------------------------------------
1 | // Package review is a set of API functions to provide an ability to make reviews for a particular product
2 | package review
3 |
4 | import (
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // Package global constants
9 | const (
10 | ConstReviewCollectionName = "review"
11 | ConstRatingCollectionName = "rating"
12 |
13 | ConstErrorModule = "product/review"
14 | ConstErrorLevel = env.ConstErrorLevelActor
15 | )
16 |
--------------------------------------------------------------------------------
/app/actors/reporting/init.go:
--------------------------------------------------------------------------------
1 | package reporting
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | )
6 |
7 | func init() {
8 | api.RegisterOnRestServiceStart(setupAPI)
9 | }
10 |
--------------------------------------------------------------------------------
/app/actors/rts/config.go:
--------------------------------------------------------------------------------
1 | package rts
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | func setupConfig() error {
8 |
9 | config := env.GetConfig()
10 | if config == nil {
11 | err := env.ErrorNew(ConstErrorModule, env.ConstErrorLevelStartStop, "a0c79b8f-5782-40bf-bae9-f0108e38d344", "Error configuring rts module")
12 | return env.ErrorDispatch(err)
13 | }
14 |
15 | err := config.RegisterItem(env.StructConfigItem{
16 | Path: ConstConfigPathCheckoutPath,
17 | Value: "/checkout",
18 | Type: env.ConstConfigTypeText,
19 | Editor: "",
20 | Options: nil,
21 | Label: "Checkout page path",
22 | Description: "",
23 | Image: "",
24 | }, nil)
25 |
26 | if err != nil {
27 | return env.ErrorDispatch(err)
28 | }
29 |
30 | return nil
31 | }
32 |
--------------------------------------------------------------------------------
/app/actors/seo/decl.go:
--------------------------------------------------------------------------------
1 | // Package seo implements a set of API intended to provide SEO optimizations
2 | package seo
3 |
4 | import (
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // Package global constants
10 | const (
11 | ConstCollectionNameURLRewrites = "url_rewrites"
12 |
13 | ConstSitemapFilePath = "sitemap.xml"
14 | ConstSitemapExpireSec = 60 * 60 * 24
15 |
16 | ConstErrorModule = "seo"
17 | ConstErrorLevel = env.ConstErrorLevelActor
18 | )
19 |
20 | // DefaultSEOItem is a default implementer of InterfaceSEOItem
21 | type DefaultSEOItem struct {
22 | id string
23 |
24 | URL string
25 | Rewrite string
26 |
27 | Type string
28 | Title string
29 | MetaKeywords string
30 | MetaDescription string
31 | }
32 |
33 | // DefaultSEOCollection is a default implementer of InterfaceSEOCollection
34 | type DefaultSEOCollection struct {
35 | listCollection db.InterfaceDBCollection
36 | listExtraAtributes []string
37 | }
38 |
--------------------------------------------------------------------------------
/app/actors/seo/helpers.go:
--------------------------------------------------------------------------------
1 | package seo
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 |
6 | "github.com/ottemo/commerce/app/models"
7 | "github.com/ottemo/commerce/app/models/seo"
8 | )
9 |
10 | // GetSEOItemCollectionModel retrieves current InterfaceSEOCollection model implementation
11 | func GetSEOItemCollectionModel() (seo.InterfaceSEOCollection, error) {
12 | model, err := models.GetModel(ConstCollectionNameURLRewrites)
13 | if err != nil {
14 | return nil, env.ErrorDispatch(err)
15 | }
16 |
17 | seoItemCollectionModel, ok := model.(seo.InterfaceSEOCollection)
18 | if !ok {
19 | return nil, env.ErrorNew(
20 | ConstErrorModule,
21 | ConstErrorLevel,
22 | "2198576e-2bf4-4631-a8b3-52b6f661f693",
23 | "model "+model.GetImplementationName()+" is not 'InterfaceSEOCollection' capable")
24 | }
25 |
26 | return seoItemCollectionModel, nil
27 | }
28 |
--------------------------------------------------------------------------------
/app/actors/seo/listable.go:
--------------------------------------------------------------------------------
1 | package seo
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/seo"
6 | )
7 |
8 | // GetCollection returns collection of current instance type
9 | func (it *DefaultSEOItem) GetCollection() models.InterfaceCollection {
10 | model, err := models.GetModel(ConstCollectionNameURLRewrites)
11 | if err != nil {
12 | return nil
13 | }
14 | if result, ok := model.(seo.InterfaceSEOCollection); ok {
15 | return result
16 | }
17 |
18 | return nil
19 | }
20 |
--------------------------------------------------------------------------------
/app/actors/shipping/fedex/init.go:
--------------------------------------------------------------------------------
1 | package fedex
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models/checkout"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // init makes package self-initialization routine
9 | func init() {
10 | if err := checkout.RegisterShippingMethod(new(FedEx)); err != nil {
11 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "619e374e-c247-4db5-81fd-2baf8dd6f9f6", err.Error())
12 | }
13 | env.RegisterOnConfigStart(setupConfig)
14 | }
15 |
--------------------------------------------------------------------------------
/app/actors/shipping/flatrate/decl.go:
--------------------------------------------------------------------------------
1 | // Package flatrate is a Flat Rate implementation of shipping method interface declared in
2 | // "github.com/ottemo/commerce/app/models/checkout" package
3 | package flatrate
4 |
5 | import (
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // Package global constants
10 | const (
11 | ConstShippingCode = "flat_rate"
12 | ConstShippingName = "Flat Rate"
13 |
14 | ConstConfigPathGroup = "shipping.flat_rate"
15 |
16 | ConstConfigPathEnabled = "shipping.flat_rate.enabled"
17 | ConstConfigPathRates = "shipping.flat_rate.rates"
18 |
19 | ConstErrorModule = "shipping/flatrate"
20 | ConstErrorLevel = env.ConstErrorLevelActor
21 | )
22 |
23 | // Package global variables
24 | var (
25 | flatRates []interface{}
26 | )
27 |
28 | // ShippingMethod is a implementer of InterfaceShippingMethod for a "Flat Rate" shipping method
29 | type ShippingMethod struct{}
30 |
--------------------------------------------------------------------------------
/app/actors/shipping/flatrate/init.go:
--------------------------------------------------------------------------------
1 | package flatrate
2 |
3 | import (
4 | "github.com/ottemo/commerce/app"
5 | "github.com/ottemo/commerce/app/models/checkout"
6 | "github.com/ottemo/commerce/env"
7 | "github.com/ottemo/commerce/utils"
8 | )
9 |
10 | // init makes package self-initialization routine
11 | func init() {
12 | instance := new(ShippingMethod)
13 |
14 | app.OnAppStart(onAppStart)
15 | if err := checkout.RegisterShippingMethod(instance); err != nil {
16 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "66cfb31e-66ff-43e8-bcd3-ddff50af3249", err.Error())
17 | }
18 |
19 | env.RegisterOnConfigStart(setupConfig)
20 | }
21 |
22 | // onAppStart makes module initialization on application startup
23 | func onAppStart() error {
24 |
25 | rules, err := utils.DecodeJSONToArray(env.ConfigGetValue(ConstConfigPathRates))
26 | if err != nil {
27 | rules = make([]interface{}, 0)
28 | }
29 |
30 | flatRates = rules
31 |
32 | return nil
33 | }
34 |
--------------------------------------------------------------------------------
/app/actors/shipping/flatweight/decl.go:
--------------------------------------------------------------------------------
1 | package flatweight
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 |
6 | "github.com/ottemo/commerce/app/models/checkout"
7 | )
8 |
9 | const (
10 | ConstShippingCode = "flat_weight"
11 | ConstShippingName = "Flat Weight"
12 |
13 | ConstConfigPathGroup = "shipping.flat_weight"
14 | ConstConfigPathEnabled = "shipping.flat_weight.enabled"
15 | ConstConfigPathRates = "shipping.flat_weight.rates"
16 |
17 | ConstErrorModule = "shipping/flatweight"
18 | ConstErrorLevel = env.ConstErrorLevelActor
19 | )
20 |
21 | // Package global vars
22 | var (
23 | rates Rates
24 | )
25 |
26 | // ShippingMethod is a implementer of InterfaceShippingMethod for a "Flat Weight" shipping method
27 | type ShippingMethod struct{}
28 |
29 | type Rates []Rate
30 |
31 | type Rate struct {
32 | Title string
33 | Code string
34 | Price float64
35 | WeightFrom float64
36 | WeightTo float64
37 | AllowedCountries string
38 | BannedCountries string
39 | }
40 |
41 | func (it Rate) validForWeight(weight float64) bool {
42 | return weight >= it.WeightFrom && weight < it.WeightTo
43 | }
44 |
45 | func (it Rate) toCheckoutStruct() checkout.StructShippingRate {
46 | return checkout.StructShippingRate{
47 | Code: it.Code,
48 | Name: it.Title,
49 | Price: it.Price,
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/actors/shipping/flatweight/init.go:
--------------------------------------------------------------------------------
1 | package flatweight
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models/checkout"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | func init() {
9 | // rates global is auto-populated via the config declaration
10 | env.RegisterOnConfigStart(setupConfig)
11 |
12 | i := new(ShippingMethod)
13 | if err := checkout.RegisterShippingMethod(i); err != nil {
14 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "30e9fc93-1841-4429-b5a1-c7c6cf7cd3b7", err.Error())
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/actors/shipping/usps/init.go:
--------------------------------------------------------------------------------
1 | package usps
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 |
6 | "github.com/ottemo/commerce/app/models/checkout"
7 | )
8 |
9 | // init makes package self-initialization routine before app start
10 | func init() {
11 | instance := new(USPS)
12 |
13 | if err := checkout.RegisterShippingMethod(instance); err != nil {
14 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "16fcb0df-c6ed-4ae2-bb4b-53468e587685", err.Error())
15 | }
16 |
17 | env.RegisterOnConfigStart(setupConfig)
18 | }
19 |
--------------------------------------------------------------------------------
/app/actors/stock/helpers.go:
--------------------------------------------------------------------------------
1 | package stock
2 |
3 | import (
4 | "github.com/ottemo/commerce/utils"
5 | )
6 |
7 | // haveInventoryOptionsDuplicates checks inventory for duplicates
8 | func haveInventoryOptionsDuplicates(inventory interface{}) bool {
9 | var inventoryArray = utils.InterfaceToArray(inventory)
10 |
11 | for idxA, itemA := range inventoryArray {
12 | var itemMapA = utils.InterfaceToMap(itemA)
13 | if _, present := itemMapA["options"]; present {
14 | var optionsA = utils.InterfaceToMap(itemMapA["options"])
15 | for idxB, itemB := range inventoryArray {
16 | var itemMapB = utils.InterfaceToMap(itemB)
17 | if idxB > idxA {
18 | if _, present := itemMapB["options"]; present {
19 | var optionsB = utils.InterfaceToMap(itemMapB["options"])
20 | var isEqual = utils.MatchMapAValuesToMapB(optionsA, optionsB) && utils.MatchMapAValuesToMapB(optionsB, optionsA)
21 |
22 | if isEqual {
23 | return true
24 | }
25 | }
26 | }
27 | }
28 | }
29 | }
30 |
31 | return false
32 | }
33 |
--------------------------------------------------------------------------------
/app/actors/stock/i_listable.go:
--------------------------------------------------------------------------------
1 | package stock
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/stock"
6 | )
7 |
8 | // GetCollection returns collection of current instance type
9 | func (it *DefaultStock) GetCollection() models.InterfaceCollection {
10 | model, err := models.GetModel(stock.ConstModelNameStockCollection)
11 | if err != nil {
12 | return nil
13 | }
14 | if result, ok := model.(stock.InterfaceStockCollection); ok {
15 | return result
16 | }
17 |
18 | return nil
19 | }
20 |
--------------------------------------------------------------------------------
/app/actors/stock/init.go:
--------------------------------------------------------------------------------
1 | package stock
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | "github.com/ottemo/commerce/app/models"
8 | "github.com/ottemo/commerce/app/models/stock"
9 | )
10 |
11 | // init makes package self-initialization routine
12 | func init() {
13 |
14 | instance := new(DefaultStock)
15 | var _ stock.InterfaceStock = instance
16 | if err := models.RegisterModel(stock.ConstModelNameStock, instance); err != nil {
17 | _ = env.ErrorDispatch(err)
18 | }
19 |
20 | stockCollectionInstance := new(DefaultStockCollection)
21 | var _ stock.InterfaceStockCollection = stockCollectionInstance
22 | if err := models.RegisterModel(stock.ConstModelNameStockCollection, stockCollectionInstance); err != nil {
23 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "42552c35-a4ef-40e6-aa38-5d69d4e92578", err.Error())
24 | }
25 |
26 | stockDelegate = new(StockDelegate)
27 | api.RegisterOnRestServiceStart(setupAPI)
28 | db.RegisterOnDatabaseStart(setupDB)
29 | env.RegisterOnConfigStart(setupConfig)
30 | }
31 |
32 | // setupDB prepares system database for package usage
33 | func setupDB() error {
34 |
35 | if collection, err := db.GetCollection(ConstCollectionNameStock); err == nil {
36 | if err := collection.AddColumn("product_id", db.ConstTypeID, true); err != nil {
37 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "d7641743-7d3a-4e1e-9627-fc4b1fad85d1", err.Error())
38 | }
39 | if err := collection.AddColumn("options", db.ConstTypeJSON, true); err != nil {
40 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "7516f60e-83f6-4b72-baa8-ae5c698f1a81", err.Error())
41 | }
42 | if err := collection.AddColumn("qty", db.ConstTypeInteger, false); err != nil {
43 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "a1ce04d7-7a61-4318-a50f-5e3113b6183d", err.Error())
44 | }
45 | } else {
46 | return env.ErrorDispatch(err)
47 | }
48 |
49 | return nil
50 | }
51 |
--------------------------------------------------------------------------------
/app/actors/subscription/collection.i_model.go:
--------------------------------------------------------------------------------
1 | package subscription
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/subscription"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns model name for the Subscription Collection
11 | func (it *DefaultSubscriptionCollection) GetModelName() string {
12 | return subscription.ConstModelNameSubscriptionCollection
13 | }
14 |
15 | // GetImplementationName returns model implementation name for the Subscription Collection
16 | func (it *DefaultSubscriptionCollection) GetImplementationName() string {
17 | return "Default" + subscription.ConstModelNameSubscriptionCollection
18 | }
19 |
20 | // New returns new instance of model implementation object for the Subscription Collection
21 | func (it *DefaultSubscriptionCollection) New() (models.InterfaceModel, error) {
22 | dbCollection, err := db.GetCollection(ConstCollectionNameSubscription)
23 | if err != nil {
24 | return nil, env.ErrorDispatch(err)
25 | }
26 |
27 | return &DefaultSubscriptionCollection{listCollection: dbCollection, listExtraAtributes: make([]string, 0)}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/subscription/collection.i_subscription_collection.go:
--------------------------------------------------------------------------------
1 | package subscription
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | "github.com/ottemo/commerce/env"
6 |
7 | "github.com/ottemo/commerce/app/models/subscription"
8 | )
9 |
10 | // GetDBCollection returns database collection for the Subscription
11 | func (it *DefaultSubscriptionCollection) GetDBCollection() db.InterfaceDBCollection {
12 | return it.listCollection
13 | }
14 |
15 | // ListSubscriptions returns list of subscription model items in the Subscription Collection
16 | func (it *DefaultSubscriptionCollection) ListSubscriptions() []subscription.InterfaceSubscription {
17 | var result []subscription.InterfaceSubscription
18 |
19 | dbRecords, err := it.listCollection.Load()
20 | if err != nil {
21 | return result
22 | }
23 |
24 | for _, recordData := range dbRecords {
25 | subscriptionModel, err := subscription.GetSubscriptionModel()
26 | if err != nil {
27 | return result
28 | }
29 | if err := subscriptionModel.FromHashMap(recordData); err != nil {
30 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "da49add1-620a-40b3-940d-075c190f7c9a", err.Error())
31 | }
32 |
33 | result = append(result, subscriptionModel)
34 | }
35 |
36 | return result
37 | }
38 |
--------------------------------------------------------------------------------
/app/actors/subscription/decl.go:
--------------------------------------------------------------------------------
1 | // Package subscription implements base subscription functionality
2 | package subscription
3 |
4 | import (
5 | "time"
6 |
7 | "github.com/ottemo/commerce/app/models/checkout"
8 | "github.com/ottemo/commerce/app/models/subscription"
9 | "github.com/ottemo/commerce/db"
10 | "github.com/ottemo/commerce/env"
11 | )
12 |
13 | // Package global constants
14 | const (
15 | ConstErrorModule = "subscription"
16 | ConstErrorLevel = env.ConstErrorLevelActor
17 |
18 | ConstCollectionNameSubscription = "subscription"
19 |
20 | ConstTimeDay = time.Hour * 24
21 |
22 | ConstSchedulerTaskName = "subscriptionProcess"
23 | )
24 |
25 | var (
26 | subscriptionProducts = make([]string, 0) // stores id's of products that should be subscriptional
27 | subscriptionEnabled = false
28 | )
29 |
30 | // DefaultSubscription struct to hold subscription information and represent
31 | // default implementer of InterfaceSubscription
32 | type DefaultSubscription struct {
33 | id string
34 |
35 | VisitorID string
36 | OrderID string
37 |
38 | items []subscription.StructSubscriptionItem
39 |
40 | CustomerEmail string
41 | CustomerName string
42 |
43 | Status string
44 | State string
45 | ActionDate time.Time
46 | Period int
47 |
48 | ShippingAddress map[string]interface{}
49 | BillingAddress map[string]interface{}
50 |
51 | ShippingMethodCode string
52 |
53 | ShippingRate checkout.StructShippingRate
54 |
55 | // should be stored credit card info with payment method in it
56 | PaymentInstrument map[string]interface{}
57 |
58 | LastSubmit time.Time
59 |
60 | CreatedAt time.Time
61 | UpdatedAt time.Time
62 |
63 | Info map[string]interface{}
64 | }
65 |
66 | // DefaultSubscriptionCollection is a default implementer of InterfaceSubscriptionCollection
67 | type DefaultSubscriptionCollection struct {
68 | listCollection db.InterfaceDBCollection
69 | listExtraAtributes []string
70 | }
71 |
--------------------------------------------------------------------------------
/app/actors/subscription/subscription.i_listable.go:
--------------------------------------------------------------------------------
1 | package subscription
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/subscription"
6 | )
7 |
8 | // GetCollection returns collection of current instance type
9 | func (it *DefaultSubscription) GetCollection() models.InterfaceCollection {
10 | model, _ := models.GetModel(subscription.ConstModelNameSubscriptionCollection)
11 | if result, ok := model.(subscription.InterfaceSubscriptionCollection); ok {
12 | return result
13 | }
14 |
15 | return nil
16 | }
17 |
--------------------------------------------------------------------------------
/app/actors/subscription/subscription.i_model.go:
--------------------------------------------------------------------------------
1 | package subscription
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/subscription"
6 | )
7 |
8 | // GetModelName returns model name for the Subscription
9 | func (it *DefaultSubscription) GetModelName() string {
10 | return subscription.ConstModelNameSubscription
11 | }
12 |
13 | // GetImplementationName returns model implementation name for the Subscription
14 | func (it *DefaultSubscription) GetImplementationName() string {
15 | return "Default" + subscription.ConstModelNameSubscription
16 | }
17 |
18 | // New returns new instance of model implementation object for the Subscription
19 | func (it *DefaultSubscription) New() (models.InterfaceModel, error) {
20 | return &DefaultSubscription{Info: make(map[string]interface{})}, nil
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/subscription/subscription.i_storable.go:
--------------------------------------------------------------------------------
1 | package subscription
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | "github.com/ottemo/commerce/env"
6 | "time"
7 | )
8 |
9 | // GetID returns current ID of the Subscription
10 | func (it *DefaultSubscription) GetID() string {
11 | return it.id
12 | }
13 |
14 | // SetID sets current ID of the current Subscription
15 | func (it *DefaultSubscription) SetID(NewID string) error {
16 | it.id = NewID
17 | return nil
18 | }
19 |
20 | // Load will retrieve the Subscription information from database
21 | func (it *DefaultSubscription) Load(ID string) error {
22 |
23 | collection, err := db.GetCollection(ConstCollectionNameSubscription)
24 | if err != nil {
25 | return env.ErrorDispatch(err)
26 | }
27 |
28 | values, err := collection.LoadByID(ID)
29 | if err != nil {
30 | return env.ErrorDispatch(err)
31 | }
32 |
33 | err = it.FromHashMap(values)
34 | return env.ErrorDispatch(err)
35 | }
36 |
37 | // Delete removes current Subscription from the database
38 | func (it *DefaultSubscription) Delete() error {
39 |
40 | collection, err := db.GetCollection(ConstCollectionNameSubscription)
41 | if err != nil {
42 | return env.ErrorDispatch(err)
43 | }
44 |
45 | err = collection.DeleteByID(it.GetID())
46 | return env.ErrorDispatch(err)
47 | }
48 |
49 | // Save stores current Subscription to the database
50 | func (it *DefaultSubscription) Save() error {
51 |
52 | collection, err := db.GetCollection(ConstCollectionNameSubscription)
53 | if err != nil {
54 | return env.ErrorDispatch(err)
55 | }
56 |
57 | // checking required fields for creating new subscription
58 | // if err := it.Validate(); err != nil {
59 | // return env.ErrorDispatch(err)
60 | // }
61 |
62 | // update time depend values
63 | currentTime := time.Now()
64 | if it.CreatedAt.IsZero() {
65 | it.CreatedAt = currentTime
66 | }
67 | it.UpdatedAt = currentTime
68 |
69 | storingValues := it.ToHashMap()
70 |
71 | // saving operation
72 | newID, err := collection.Save(storingValues)
73 | if err != nil {
74 | return env.ErrorDispatch(err)
75 | }
76 |
77 | return it.SetID(newID)
78 | }
79 |
--------------------------------------------------------------------------------
/app/actors/swatch/decl.go:
--------------------------------------------------------------------------------
1 | // Package swatch is a default implementation of product swatches
2 | package swatch
3 |
4 | import (
5 | "github.com/ottemo/commerce/media"
6 | )
7 |
8 | // Package global constants
9 | const (
10 | ConstErrorModule = "swatch"
11 |
12 | ConstStorageModel = "swatch"
13 | ConstStorageObjectID = "media"
14 | ConstStorageMediaType = "image"
15 |
16 | ConstImageDefaultFormat = "png"
17 | ConstImageDefaultExtention = "png"
18 | )
19 |
20 | var (
21 | mediaStorage media.InterfaceMediaStorage
22 | )
23 |
--------------------------------------------------------------------------------
/app/actors/swatch/init.go:
--------------------------------------------------------------------------------
1 | package swatch
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/app"
6 | "github.com/ottemo/commerce/env"
7 | "github.com/ottemo/commerce/media"
8 | )
9 |
10 | // init makes package self-initialization routine
11 | func init() {
12 | app.OnAppStart(onAppStart)
13 | api.RegisterOnRestServiceStart(setupAPI)
14 | }
15 |
16 | func onAppStart() error {
17 |
18 | var err error
19 | mediaStorage, err = media.GetMediaStorage()
20 | if err != nil {
21 | return env.ErrorDispatch(err)
22 | }
23 |
24 | return nil
25 | }
26 |
--------------------------------------------------------------------------------
/app/actors/tax/decl.go:
--------------------------------------------------------------------------------
1 | // Package tax is a implementation of tax interface declared in
2 | // "github.com/ottemo/commerce/app/models/checkout" package
3 | package tax
4 |
5 | import (
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // Package global constants
10 | const (
11 | ConstErrorModule = "tax"
12 | ConstErrorLevel = env.ConstErrorLevelActor
13 |
14 | ConstPriorityValue = 2.50
15 |
16 | ConstProductTaxableAttribute = "taxable"
17 | )
18 |
19 | var priority float64
20 |
21 | // DefaultTax is a default implementer of InterfaceTax
22 | type DefaultTax struct{}
23 |
--------------------------------------------------------------------------------
/app/actors/visitor/address/address.i_listable.go:
--------------------------------------------------------------------------------
1 | package address
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/visitor"
6 | )
7 |
8 | // GetCollection returns collection of current instance type
9 | func (it *DefaultVisitorAddress) GetCollection() models.InterfaceCollection {
10 | model, _ := models.GetModel(visitor.ConstModelNameVisitorAddressCollection)
11 | if result, ok := model.(visitor.InterfaceVisitorAddressCollection); ok {
12 | return result
13 | }
14 |
15 | return nil
16 | }
17 |
--------------------------------------------------------------------------------
/app/actors/visitor/address/address.i_model.go:
--------------------------------------------------------------------------------
1 | package address
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/visitor"
6 | )
7 |
8 | // GetModelName returns the Visitor Address Model
9 | func (it *DefaultVisitorAddress) GetModelName() string {
10 | return visitor.ConstModelNameVisitorAddress
11 | }
12 |
13 | // GetImplementationName returns the Implementation name
14 | func (it *DefaultVisitorAddress) GetImplementationName() string {
15 | return "Default" + visitor.ConstModelNameVisitorAddress
16 | }
17 |
18 | // New creates a new Visitor Address interface
19 | func (it *DefaultVisitorAddress) New() (models.InterfaceModel, error) {
20 | return &DefaultVisitorAddress{}, nil
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/visitor/address/address.i_visitor_address.go:
--------------------------------------------------------------------------------
1 | package address
2 |
3 | // GetVisitorID returns the Visitor ID for the Visitor Address
4 | func (it *DefaultVisitorAddress) GetVisitorID() string { return it.visitorID }
5 |
6 | // GetFirstName returns the First Name of the Visitor Address
7 | func (it *DefaultVisitorAddress) GetFirstName() string { return it.FirstName }
8 |
9 | // GetLastName returns the Last Name of the Visitor Address
10 | func (it *DefaultVisitorAddress) GetLastName() string { return it.LastName }
11 |
12 | // GetCompany will return the Company attribute of the Visitor Address
13 | func (it *DefaultVisitorAddress) GetCompany() string { return it.Company }
14 |
15 | // GetCountry will return the Country attribute of the Visitor Address
16 | func (it *DefaultVisitorAddress) GetCountry() string { return it.Country }
17 |
18 | // GetState will return the State attribute of the Visitor Address
19 | func (it *DefaultVisitorAddress) GetState() string { return it.State }
20 |
21 | // GetCity will return the City attribute of the Visitor Address
22 | func (it *DefaultVisitorAddress) GetCity() string { return it.City }
23 |
24 | // GetAddress will return the full Address of the current Visitor Address
25 | func (it *DefaultVisitorAddress) GetAddress() string { return it.AddressLine1 + " " + it.AddressLine2 }
26 |
27 | // GetAddressLine1 will return the Line 1 attribute of the Visitor Address
28 | func (it *DefaultVisitorAddress) GetAddressLine1() string { return it.AddressLine1 }
29 |
30 | // GetAddressLine2 will return the Line 2 attribute of the Visitor Address
31 | func (it *DefaultVisitorAddress) GetAddressLine2() string { return it.AddressLine2 }
32 |
33 | // GetPhone will return the phone attribute of the Visitor Address
34 | func (it *DefaultVisitorAddress) GetPhone() string { return it.Phone }
35 |
36 | // GetZipCode will return the zip code attribute of the Visitor Address
37 | func (it *DefaultVisitorAddress) GetZipCode() string { return it.ZipCode }
38 |
--------------------------------------------------------------------------------
/app/actors/visitor/address/collection.i_model.go:
--------------------------------------------------------------------------------
1 | package address
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/visitor"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns the Visitor Address model
11 | func (it *DefaultVisitorAddressCollection) GetModelName() string {
12 | return visitor.ConstModelNameVisitorAddress
13 | }
14 |
15 | // GetImplementationName returns the Visitor Address implementation name
16 | func (it *DefaultVisitorAddressCollection) GetImplementationName() string {
17 | return "Default" + visitor.ConstModelNameVisitorAddress
18 | }
19 |
20 | // New creates a new Visitor Address Collection
21 | func (it *DefaultVisitorAddressCollection) New() (models.InterfaceModel, error) {
22 | dbCollection, err := db.GetCollection(ConstCollectionNameVisitorAddress)
23 | if err != nil {
24 | return nil, env.ErrorDispatch(err)
25 | }
26 |
27 | return &DefaultVisitorAddressCollection{listCollection: dbCollection, listExtraAtributes: make([]string, 0)}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/visitor/address/collection.i_visitor_address_collection.go:
--------------------------------------------------------------------------------
1 | package address
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | "github.com/ottemo/commerce/env"
6 |
7 | "github.com/ottemo/commerce/app/models/visitor"
8 | )
9 |
10 | // GetDBCollection returns the database collection of the Visitor Address
11 | func (it *DefaultVisitorAddressCollection) GetDBCollection() db.InterfaceDBCollection {
12 | return it.listCollection
13 | }
14 |
15 | // ListVisitorsAddresses returns list of visitor model items for the Visitor Address
16 | func (it *DefaultVisitorAddressCollection) ListVisitorsAddresses() []visitor.InterfaceVisitorAddress {
17 | var result []visitor.InterfaceVisitorAddress
18 |
19 | dbRecords, err := it.listCollection.Load()
20 | if err != nil {
21 | return result
22 | }
23 |
24 | for _, recordData := range dbRecords {
25 | visitorAddressModel, err := visitor.GetVisitorAddressModel()
26 | if err != nil {
27 | return result
28 | }
29 | if err := visitorAddressModel.FromHashMap(recordData); err != nil {
30 | _ = env.ErrorDispatch(err)
31 | }
32 |
33 | result = append(result, visitorAddressModel)
34 | }
35 |
36 | return result
37 | }
38 |
--------------------------------------------------------------------------------
/app/actors/visitor/address/decl.go:
--------------------------------------------------------------------------------
1 | // Package address is a default implementation of models/visitor package visitor address related interfaces
2 | package address
3 |
4 | import (
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // Package global constants
10 | const (
11 | ConstCollectionNameVisitorAddress = "visitor_address"
12 |
13 | ConstErrorModule = "visitor/address"
14 | ConstErrorLevel = env.ConstErrorLevelActor
15 | )
16 |
17 | // DefaultVisitorAddress is a default implementer of InterfaceVisitorAddress
18 | type DefaultVisitorAddress struct {
19 | id string
20 | visitorID string
21 |
22 | FirstName string
23 | LastName string
24 |
25 | Company string
26 |
27 | Country string
28 | State string
29 | City string
30 |
31 | AddressLine1 string
32 | AddressLine2 string
33 |
34 | Phone string
35 | ZipCode string
36 | }
37 |
38 | // DefaultVisitorAddressCollection is a default implementer of InterfaceVisitorAddressCollection
39 | type DefaultVisitorAddressCollection struct {
40 | listCollection db.InterfaceDBCollection
41 | listExtraAtributes []string
42 | }
43 |
--------------------------------------------------------------------------------
/app/actors/visitor/collection.i_model.go:
--------------------------------------------------------------------------------
1 | package visitor
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/visitor"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns model name for the Visitor Collection
11 | func (it *DefaultVisitorCollection) GetModelName() string {
12 | return visitor.ConstModelNameVisitorCollection
13 | }
14 |
15 | // GetImplementationName returns model implementation name for the Visitor Collection
16 | func (it *DefaultVisitorCollection) GetImplementationName() string {
17 | return "Default" + visitor.ConstModelNameVisitorCollection
18 | }
19 |
20 | // New returns new instance of model implementation object for the Visitor Collection
21 | func (it *DefaultVisitorCollection) New() (models.InterfaceModel, error) {
22 | dbCollection, err := db.GetCollection(ConstCollectionNameVisitor)
23 | if err != nil {
24 | return nil, env.ErrorDispatch(err)
25 | }
26 |
27 | return &DefaultVisitorCollection{listCollection: dbCollection, listExtraAtributes: make([]string, 0)}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/visitor/collection.i_visitor_collection.go:
--------------------------------------------------------------------------------
1 | package visitor
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | "github.com/ottemo/commerce/env"
6 |
7 | "github.com/ottemo/commerce/app/models/visitor"
8 | )
9 |
10 | // GetDBCollection returns database collection for the Visitor
11 | func (it *DefaultVisitorCollection) GetDBCollection() db.InterfaceDBCollection {
12 | return it.listCollection
13 | }
14 |
15 | // ListVisitors returns list of visitor model items in the Visitor Collection
16 | func (it *DefaultVisitorCollection) ListVisitors() []visitor.InterfaceVisitor {
17 | var result []visitor.InterfaceVisitor
18 |
19 | dbRecords, err := it.listCollection.Load()
20 | if err != nil {
21 | return result
22 | }
23 |
24 | for _, recordData := range dbRecords {
25 | visitorModel, err := visitor.GetVisitorModel()
26 | if err != nil {
27 | return result
28 | }
29 | if err := visitorModel.FromHashMap(recordData); err != nil {
30 | _ = env.ErrorNew(ConstErrorModule, ConstErrorLevel, "7c4664a6-52eb-419f-adec-df7b7fd146a1", err.Error())
31 | }
32 |
33 | result = append(result, visitorModel)
34 | }
35 |
36 | return result
37 | }
38 |
--------------------------------------------------------------------------------
/app/actors/visitor/config.go:
--------------------------------------------------------------------------------
1 | package visitor
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // setupConfig setups package configuration values for a system
8 | func setupConfig() error {
9 | config := env.GetConfig()
10 | if config == nil {
11 | err := env.ErrorNew(ConstErrorModule, env.ConstErrorLevelStartStop, "b196712b-99c3-4510-bd58-935c5eedfc9f", "Unable to obtain configuration")
12 | return env.ErrorDispatch(err)
13 | }
14 |
15 | err := config.RegisterItem(env.StructConfigItem{
16 | Path: ConstConfigPathLostPasswordEmailSubject,
17 | Value: "",
18 | Type: env.ConstConfigTypeVarchar,
19 | Editor: "line_text",
20 | Options: "",
21 | Label: "Lost Password Email Subject",
22 | Description: "",
23 | Image: "",
24 | }, nil)
25 | if err != nil {
26 | return env.ErrorDispatch(err)
27 | }
28 |
29 | err = config.RegisterItem(env.StructConfigItem{
30 | Path: ConstConfigPathLostPasswordEmailTemplate,
31 | Value: "",
32 | Type: env.ConstConfigTypeText,
33 | Editor: "multiline_text",
34 | Options: "",
35 | Label: "Lost Password Email Template",
36 | Description: "",
37 | Image: "",
38 | }, nil)
39 |
40 | if err != nil {
41 | return env.ErrorDispatch(err)
42 | }
43 |
44 | return nil
45 | }
46 |
--------------------------------------------------------------------------------
/app/actors/visitor/decl.go:
--------------------------------------------------------------------------------
1 | // Package visitor is a default implementation of models/visitor package visitor related interfaces
2 | package visitor
3 |
4 | import (
5 | "time"
6 |
7 | "github.com/ottemo/commerce/app/helpers/attributes"
8 | "github.com/ottemo/commerce/app/models/visitor"
9 | "github.com/ottemo/commerce/db"
10 | "github.com/ottemo/commerce/env"
11 | )
12 |
13 | // Package global constants
14 | const (
15 | ConstCollectionNameVisitor = "visitor"
16 |
17 | ConstEmailVerifyExpire = 60 * 60 * 24
18 |
19 | ConstEmailPasswordResetExpire = 30 * 60
20 |
21 | ConstErrorModule = "visitor"
22 | ConstErrorLevel = env.ConstErrorLevelActor
23 |
24 | ConstConfigPathLostPasswordEmailSubject = "general.mail.lost_password_email_subject"
25 | ConstConfigPathLostPasswordEmailTemplate = "general.mail.lost_password_email_template"
26 | )
27 |
28 | // DefaultVisitor is a default implementer of InterfaceVisitor
29 | type DefaultVisitor struct {
30 | id string
31 |
32 | Email string
33 | FacebookID string
34 | GoogleID string
35 |
36 | FirstName string
37 | LastName string
38 |
39 | BillingAddress visitor.InterfaceVisitorAddress
40 | ShippingAddress visitor.InterfaceVisitorAddress
41 |
42 | Token visitor.InterfaceVisitorCard
43 |
44 | Password string
45 | VerificationKey string
46 |
47 | Admin bool
48 |
49 | CreatedAt time.Time
50 |
51 | *attributes.ModelCustomAttributes
52 | }
53 |
54 | // DefaultVisitorCollection is a default implementer of InterfaceVisitorCollection
55 | type DefaultVisitorCollection struct {
56 | listCollection db.InterfaceDBCollection
57 | listExtraAtributes []string
58 | }
59 |
--------------------------------------------------------------------------------
/app/actors/visitor/token/collection.i_model.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/visitor"
6 | "github.com/ottemo/commerce/db"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns the Visitor Address model
11 | func (it *DefaultVisitorCardCollection) GetModelName() string {
12 | return visitor.ConstModelNameVisitorCard
13 | }
14 |
15 | // GetImplementationName returns the Visitor Address implementation name
16 | func (it *DefaultVisitorCardCollection) GetImplementationName() string {
17 | return "Default" + visitor.ConstModelNameVisitorCard
18 | }
19 |
20 | // New creates a new Visitor Address Collection
21 | func (it *DefaultVisitorCardCollection) New() (models.InterfaceModel, error) {
22 | dbCollection, err := db.GetCollection(ConstCollectionNameVisitorToken)
23 | if err != nil {
24 | return nil, env.ErrorDispatch(err)
25 | }
26 |
27 | return &DefaultVisitorCardCollection{listCollection: dbCollection, listExtraAtributes: make([]string, 0)}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/actors/visitor/token/collection.i_visitor_card_collection.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models/visitor"
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // GetDBCollection returns the database collection of the Visitor Cards
10 | func (it *DefaultVisitorCardCollection) GetDBCollection() db.InterfaceDBCollection {
11 | return it.listCollection
12 | }
13 |
14 | // ListVisitorsCards returns list of visitor model items for the Visitor Cards
15 | func (it *DefaultVisitorCardCollection) ListVisitorsCards() []visitor.InterfaceVisitorCard {
16 | var result []visitor.InterfaceVisitorCard
17 |
18 | dbRecords, err := it.listCollection.Load()
19 | if err != nil {
20 | return result
21 | }
22 |
23 | for _, recordData := range dbRecords {
24 | visitorCardModel, err := visitor.GetVisitorCardModel()
25 | if err != nil {
26 | return result
27 | }
28 | if err := visitorCardModel.FromHashMap(recordData); err != nil {
29 | _ = env.ErrorDispatch(err)
30 | }
31 |
32 | result = append(result, visitorCardModel)
33 | }
34 |
35 | return result
36 | }
37 |
--------------------------------------------------------------------------------
/app/actors/visitor/token/decl.go:
--------------------------------------------------------------------------------
1 | // Package token allows to create and use tokens
2 | package token
3 |
4 | import (
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | "time"
8 | )
9 |
10 | // Package global constants
11 | const (
12 | ConstCollectionNameVisitorToken = "visitor_token"
13 |
14 | ConstErrorModule = "visitor/token"
15 | ConstErrorLevel = env.ConstErrorLevelActor
16 | )
17 |
18 | // DefaultVisitorCard is a default implementer of InterfaceVisitorCard
19 | type DefaultVisitorCard struct {
20 | id string
21 | visitorID string
22 |
23 | Holder string
24 | Payment string
25 |
26 | Type string
27 | Number string
28 |
29 | ExpirationDate string
30 | ExpirationMonth int
31 | ExpirationYear int
32 |
33 | CreatedAt time.Time
34 | TokenUpdated time.Time
35 |
36 | tokenID string
37 | customerID string
38 | }
39 |
40 | // DefaultVisitorCardCollection is a default implementer of InterfaceVisitorCardCollection
41 | type DefaultVisitorCardCollection struct {
42 | listCollection db.InterfaceDBCollection
43 | listExtraAtributes []string
44 | }
45 |
--------------------------------------------------------------------------------
/app/actors/visitor/token/token.i_model.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/visitor"
6 | )
7 |
8 | // GetModelName returns the Visitor Address Model
9 | func (it *DefaultVisitorCard) GetModelName() string {
10 | return visitor.ConstModelNameVisitorCard
11 | }
12 |
13 | // GetImplementationName returns the Implementation name
14 | func (it *DefaultVisitorCard) GetImplementationName() string {
15 | return "Default" + visitor.ConstModelNameVisitorCard
16 | }
17 |
18 | // New creates a new Visitor Address interface
19 | func (it *DefaultVisitorCard) New() (models.InterfaceModel, error) {
20 | return &DefaultVisitorCard{}, nil
21 | }
22 |
--------------------------------------------------------------------------------
/app/actors/visitor/token/token.i_storable.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | "github.com/ottemo/commerce/env"
6 | "github.com/ottemo/commerce/utils"
7 | "time"
8 | )
9 |
10 | // GetID returns the Default Visitor Token as a string
11 | func (it *DefaultVisitorCard) GetID() string {
12 | return it.id
13 | }
14 |
15 | // SetID takes a string as input and sets the ID on the Visitor Token
16 | func (it *DefaultVisitorCard) SetID(NewID string) error {
17 | it.id = NewID
18 | return nil
19 | }
20 |
21 | // Load will take Visitor Token ID and retrieve it from the database
22 | func (it *DefaultVisitorCard) Load(loadID string) error {
23 |
24 | collection, err := db.GetCollection(ConstCollectionNameVisitorToken)
25 | if err != nil {
26 | return env.ErrorDispatch(err)
27 | }
28 |
29 | dbRecord, err := collection.LoadByID(loadID)
30 | if err != nil {
31 | return env.ErrorDispatch(err)
32 | }
33 |
34 | return it.FromHashMap(dbRecord)
35 | }
36 |
37 | // Delete will remove the Visitor Token from the database
38 | func (it *DefaultVisitorCard) Delete() error {
39 |
40 | collection, err := db.GetCollection(ConstCollectionNameVisitorToken)
41 | if err != nil {
42 | return env.ErrorDispatch(err)
43 | }
44 |
45 | return collection.DeleteByID(it.GetID())
46 | }
47 |
48 | // Save will persist the Visitor Token to the database
49 | func (it *DefaultVisitorCard) Save() error {
50 |
51 | collection, err := db.GetCollection(ConstCollectionNameVisitorToken)
52 | if err != nil {
53 | return env.ErrorDispatch(err)
54 | }
55 |
56 | if it.tokenID == "" || it.Payment == "" {
57 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "dbd526dd-e0ae-4f43-a8cd-2da8ff3bba8a", "payment and token should be specified")
58 | }
59 |
60 | if utils.IsZeroTime(it.CreatedAt) && it.GetID() == "" {
61 | it.CreatedAt = time.Now()
62 | }
63 |
64 | storableValues := it.ToHashMap()
65 |
66 | newID, err := collection.Save(storableValues)
67 | if err != nil {
68 | return env.ErrorDispatch(err)
69 | }
70 |
71 | return it.SetID(newID)
72 | }
73 |
--------------------------------------------------------------------------------
/app/actors/visitor/token/token.i_visitor_card.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | "github.com/ottemo/commerce/utils"
5 | "time"
6 | )
7 |
8 | // GetVisitorID returns the Visitor ID for the Visitor Card
9 | func (it *DefaultVisitorCard) GetVisitorID() string {
10 | return it.visitorID
11 | }
12 |
13 | // GetHolderName returns the Holder of the Credit Card
14 | func (it *DefaultVisitorCard) GetHolderName() string {
15 | return it.Holder
16 | }
17 |
18 | // GetPaymentMethodCode returns the Payment method code of the Visitor Card
19 | func (it *DefaultVisitorCard) GetPaymentMethodCode() string {
20 | return it.Payment
21 | }
22 |
23 | // GetType will return the Type of the Visitor Card
24 | func (it *DefaultVisitorCard) GetType() string {
25 | return it.Type
26 | }
27 |
28 | // GetNumber will return the Number attribute of the Visitor Card
29 | func (it *DefaultVisitorCard) GetNumber() string {
30 | return it.Number
31 | }
32 |
33 | // GetExpirationDate will return the Expiration date of the Visitor Card
34 | func (it *DefaultVisitorCard) GetExpirationDate() string {
35 |
36 | if it.ExpirationDate == "" {
37 | it.ExpirationDate = utils.InterfaceToString(it.ExpirationMonth) + "/" + utils.InterfaceToString(it.ExpirationYear)
38 | }
39 |
40 | return it.ExpirationDate
41 | }
42 |
43 | // GetToken will return the Token of the Visitor Card
44 | func (it *DefaultVisitorCard) GetToken() string {
45 | return it.tokenID
46 | }
47 |
48 | // GetCustomerID will return the customer_id field of the Visitor Card
49 | func (it *DefaultVisitorCard) GetCustomerID() string {
50 | return it.customerID
51 | }
52 |
53 | // IsExpired will return Expired status of the Visitor Card
54 | func (it *DefaultVisitorCard) IsExpired() bool {
55 | current := time.Now()
56 | return it.ExpirationYear < utils.InterfaceToInt(current.Year()) || it.ExpirationMonth < utils.InterfaceToInt(current.Month())
57 | }
58 |
--------------------------------------------------------------------------------
/app/actors/visitor/visitor.i_listable.go:
--------------------------------------------------------------------------------
1 | package visitor
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/app/models/visitor"
6 | )
7 |
8 | // GetCollection returns collection of current instance type
9 | func (it *DefaultVisitor) GetCollection() models.InterfaceCollection {
10 | model, _ := models.GetModel(visitor.ConstModelNameVisitorCollection)
11 | if result, ok := model.(visitor.InterfaceVisitorCollection); ok {
12 | return result
13 | }
14 |
15 | return nil
16 | }
17 |
--------------------------------------------------------------------------------
/app/actors/visitor/visitor.i_model.go:
--------------------------------------------------------------------------------
1 | package visitor
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/helpers/attributes"
5 | "github.com/ottemo/commerce/app/models"
6 | "github.com/ottemo/commerce/app/models/visitor"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // GetModelName returns model name for the Visitor
11 | func (it *DefaultVisitor) GetModelName() string {
12 | return visitor.ConstModelNameVisitor
13 | }
14 |
15 | // GetImplementationName returns model implementation name for the Visitor
16 | func (it *DefaultVisitor) GetImplementationName() string {
17 | return "Default" + visitor.ConstModelNameVisitor
18 | }
19 |
20 | // New returns new instance of model implementation object for the Visitor
21 | func (it *DefaultVisitor) New() (models.InterfaceModel, error) {
22 |
23 | customAttributes, err := attributes.CustomAttributes(visitor.ConstModelNameVisitor, ConstCollectionNameVisitor)
24 | if err != nil {
25 | return nil, env.ErrorDispatch(err)
26 | }
27 |
28 | return &DefaultVisitor{ModelCustomAttributes: customAttributes}, nil
29 | }
30 |
--------------------------------------------------------------------------------
/app/actors/xdomain/api.go:
--------------------------------------------------------------------------------
1 | package xdomain
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/ottemo/commerce/api"
7 | )
8 |
9 | // endpoint configuration for xdomain package
10 | func setupAPI() error {
11 |
12 | service := api.GetRestService()
13 | service.GET("proxy.html", xdomainHandler)
14 |
15 | return nil
16 | }
17 |
18 | // xdomainHandler will enable the usage of xdomain instead of CORS for legacy browsers
19 | func xdomainHandler(context api.InterfaceApplicationContext) (interface{}, error) {
20 |
21 | responseWriter := context.GetResponseWriter()
22 |
23 | newline := []byte("\n")
24 |
25 | if err := context.SetResponseContentType("text/html"); err != nil {
26 | fmt.Println("06a968c4-cd17-43cf-a93e-b7317b53b883", err)
27 | }
28 |
29 | if _, err := responseWriter.Write([]byte("")); err != nil {
30 | fmt.Println("c02a9d84-3c24-4998-b060-2bbb92bea6da", err)
31 | }
32 | if _, err := responseWriter.Write(newline); err != nil {
33 | fmt.Println("3968707c-c8e4-4e91-a4ed-f5869619e1bc", err)
34 | }
35 | if _, err := responseWriter.Write([]byte("")); err != nil {
36 | fmt.Println("5b815f17-42a6-42a5-8591-82674f1a86bd", err)
37 | }
38 | if _, err := responseWriter.Write(newline); err != nil {
39 | fmt.Println("a6c11509-a68b-4920-bee2-3a8ab93ea30c", err)
40 | }
41 |
42 | return "", nil
43 | }
44 |
--------------------------------------------------------------------------------
/app/actors/xdomain/decl.go:
--------------------------------------------------------------------------------
1 | package xdomain
2 |
3 | import "github.com/ottemo/commerce/env"
4 |
5 | // xdomain package constants
6 | const (
7 | ConstErrorModule = "xdomain"
8 | ConstErrorLevel = env.ConstErrorLevelActor
9 | )
10 |
11 | // xdomain package level globals
12 | var (
13 | xdomainMasterURL = "http://*.staging.ottemo.io/" // default to all stores in staging
14 | )
15 |
--------------------------------------------------------------------------------
/app/actors/xdomain/init.go:
--------------------------------------------------------------------------------
1 | package xdomain
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // init performs self-initialization routine before app start
9 | func init() {
10 |
11 | env.RegisterOnConfigIniStart(setupIniConfig)
12 | api.RegisterOnRestServiceStart(setupAPI)
13 | }
14 |
15 | // setupIniConfig reads the setting from the ottemo.ini file
16 | func setupIniConfig() error {
17 |
18 | if iniConfig := env.GetIniConfig(); iniConfig != nil {
19 | if iniValue := iniConfig.GetValue("xdomain.master", xdomainMasterURL); iniValue != "" {
20 | xdomainMasterURL = iniValue
21 | }
22 | }
23 |
24 | return nil
25 | }
26 |
--------------------------------------------------------------------------------
/app/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 |
5 | Package app represents Ottemo application object.
6 |
7 | That package contains routines which allows other components to register callbacks on application start/end, API
8 | functions for administrator login, system configuration values, etc. So, this package contains the code addressed to
9 | application instance. Ottemo packages should address this package to interact with running application instance but not
10 | to "github.com/ottemo/foundaton" package".
11 |
12 | */
13 | package app
14 |
--------------------------------------------------------------------------------
/app/helpers/attributes/other_test.go:
--------------------------------------------------------------------------------
1 | package attributes
2 |
3 | import "testing"
4 |
5 | // TestExternalAttributes tests concurrent execution for an ExternalAttributes
6 | func TestExternalAttributes(t *testing.T) {
7 | const concurrent = 9999
8 | finished := make(chan int)
9 |
10 | routines := concurrent
11 | for i := 0; i < routines; i++ {
12 | go func(i int) {
13 | ExampleExternalAttributes()
14 |
15 | finished <- i
16 | }(i)
17 | }
18 |
19 | for routines > 0 {
20 | <-finished
21 | routines--
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/helpers/objectref/decl.go:
--------------------------------------------------------------------------------
1 | // Package objectref intended to unify and simplify a way of model instance changes tracking (currently not implemented)
2 | package objectref
3 |
4 | import (
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // Package global constants
9 | const (
10 | ConstErrorModule = "objectref"
11 | ConstErrorLevel = env.ConstErrorLevelHelper
12 | )
13 |
14 | // DBObjectRef is a object state tracking helper and implementer of InterfaceObject and InterfaceStorable
15 | type DBObjectRef struct {
16 | id string
17 |
18 | loaded bool
19 | modified bool
20 |
21 | origData map[string]interface{}
22 | currData map[string]interface{}
23 | }
24 |
25 | // MarkAsLoaded marks object instance as loaded from DB
26 | func (it *DBObjectRef) MarkAsLoaded() {
27 | it.loaded = true
28 | }
29 |
30 | // MarkAsModified marks object instance as modified
31 | func (it *DBObjectRef) MarkAsModified() {
32 | it.modified = true
33 | }
34 |
35 | // IsModified returns value of modification flag
36 | func (it *DBObjectRef) IsModified() bool {
37 | return it.modified
38 | }
39 |
40 | // IsLoaded returns value of load from DB flag
41 | func (it *DBObjectRef) IsLoaded() bool {
42 | return it.loaded
43 | }
44 |
--------------------------------------------------------------------------------
/app/helpers/objectref/i_storable.go:
--------------------------------------------------------------------------------
1 | package objectref
2 |
3 | import "github.com/ottemo/commerce/env"
4 |
5 | // GetID returns current object id
6 | func (it *DBObjectRef) GetID() string {
7 | return it.id
8 | }
9 |
10 | // SetID sets new id to current object
11 | func (it *DBObjectRef) SetID(id string) {
12 | it.id = id
13 | }
14 |
15 | // Save stores current product to DB
16 | func (it *DBObjectRef) Save() error {
17 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "d41d76c0-fa63-4418-b90e-bde64864ecaa", "not implemented")
18 | }
19 |
20 | // Load loads information from DB
21 | func (it *DBObjectRef) Load(id string) error {
22 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "d65b0e21-c51d-4460-a32a-e4f09a84f421", "not implemented")
23 | }
24 |
25 | // Delete removes current object instance from DB
26 | func (it *DBObjectRef) Delete() error {
27 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "61c0b53d-b2a4-499a-be8b-6f2101f1ab97", "not implemented")
28 | }
29 |
--------------------------------------------------------------------------------
/app/init.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // init makes package self-initialization routine
9 | func init() {
10 | env.RegisterOnConfigStart(setupConfig)
11 | api.RegisterOnRestServiceStart(setupAPI)
12 | }
13 |
--------------------------------------------------------------------------------
/app/models/blog/post/helpers.go:
--------------------------------------------------------------------------------
1 | package post
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 |
6 | "github.com/ottemo/commerce/app/models"
7 | )
8 |
9 | // GetBlogPostModel retrieves current InterfaceBlogPost model implementation
10 | func GetBlogPostModel() (InterfaceBlogPost, error) {
11 | model, err := models.GetModel(ConstModelNameBlogPost)
12 | if err != nil {
13 | return nil, env.ErrorDispatch(err)
14 | }
15 |
16 | blogPostModel, ok := model.(InterfaceBlogPost)
17 | if !ok {
18 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "d2fd8dc4-094e-4a32-8e89-d8be2f3bf1ea", "model "+model.GetImplementationName()+" is not 'InterfaceBlogPost' capable")
19 | }
20 |
21 | return blogPostModel, nil
22 | }
23 |
--------------------------------------------------------------------------------
/app/models/blog/post/interfaces.go:
--------------------------------------------------------------------------------
1 | // Package post represents abstraction of business layer blog post object
2 | package post
3 |
4 | import (
5 | "time"
6 |
7 | "github.com/ottemo/commerce/env"
8 |
9 | "github.com/ottemo/commerce/app/models"
10 | )
11 |
12 | // Package global constants
13 | const (
14 | ConstModelNameBlogPost = "BlogPost"
15 |
16 | ConstErrorModule = "blog"
17 | ConstErrorLevel = env.ConstErrorLevelModel
18 | )
19 |
20 | // InterfaceBlogPost represents interface to access business layer implementation of blog post object
21 | type InterfaceBlogPost interface {
22 | GetIdentifier() string
23 | SetIdentifier(string) error
24 | IsPublished() bool
25 | SetPublished(bool) error
26 |
27 | GetTitle() string
28 | SetTitle(string) error
29 | GetExcerpt() string
30 | SetExcerpt(string) error
31 | GetContent() string
32 | SetContent(string) error
33 |
34 | GetTags() []interface{}
35 | SetTags([]interface{}) error
36 | GetFeaturedImage() string
37 | SetFeaturedImage(string) error
38 |
39 | GetCreatedAt() time.Time
40 | SetCreatedAt(time.Time) error
41 | GetUpdatedAt() time.Time
42 | SetUpdatedAt(time.Time) error
43 |
44 | models.InterfaceModel
45 | models.InterfaceObject
46 | models.InterfaceStorable
47 | }
48 |
--------------------------------------------------------------------------------
/app/models/cart/interfaces.go:
--------------------------------------------------------------------------------
1 | // Package cart represents abstraction of business layer cart object
2 | package cart
3 |
4 | import (
5 | "time"
6 |
7 | "github.com/ottemo/commerce/app/models"
8 | "github.com/ottemo/commerce/app/models/product"
9 | "github.com/ottemo/commerce/app/models/visitor"
10 | "github.com/ottemo/commerce/env"
11 | )
12 |
13 | // Package global constants
14 | const (
15 | ConstCartModelName = "Cart"
16 | ConstSessionKeyCurrentCart = "cart_id"
17 |
18 | ConstErrorModule = "cart"
19 | ConstErrorLevel = env.ConstErrorLevelModel
20 | )
21 |
22 | // InterfaceCartItem represents interface to access business layer implementation of cart item object
23 | type InterfaceCartItem interface {
24 | GetID() string
25 | SetID(newID string) error
26 |
27 | GetIdx() int
28 | SetIdx(newIdx int) error
29 |
30 | GetProductID() string
31 | GetProduct() product.InterfaceProduct
32 |
33 | GetQty() int
34 | SetQty(qty int) error
35 |
36 | GetOptions() map[string]interface{}
37 | SetOption(optionName string, optionValue interface{}) error
38 |
39 | ValidateProduct() error
40 |
41 | GetCart() InterfaceCart
42 | }
43 |
44 | // InterfaceCart represents interface to access business layer implementation of cart object
45 | type InterfaceCart interface {
46 | AddItem(productID string, qty int, options map[string]interface{}) (InterfaceCartItem, error)
47 | RemoveItem(itemIdx int) error
48 | SetQty(itemIdx int, qty int) error
49 |
50 | GetItems() []InterfaceCartItem
51 | GetSubtotal() float64
52 |
53 | GetVisitorID() string
54 | SetVisitorID(string) error
55 |
56 | GetVisitor() visitor.InterfaceVisitor
57 | MakeCartForVisitor(visitorID string) error
58 |
59 | GetSessionID() string
60 | SetSessionID(sessionID string) error
61 |
62 | Activate() error
63 | Deactivate() error
64 | IsActive() bool
65 |
66 | GetLastUpdateTime() time.Time
67 |
68 | SetCartInfo(infoAttribute string, infoValue interface{}) error
69 | GetCartInfo() map[string]interface{}
70 |
71 | SetCustomInfo(info map[string]interface{})
72 | GetCustomInfo() map[string]interface{}
73 |
74 | ValidateCart() error
75 |
76 | models.InterfaceModel
77 | models.InterfaceStorable
78 | }
79 |
--------------------------------------------------------------------------------
/app/models/category/interfaces.go:
--------------------------------------------------------------------------------
1 | // Package category represents abstraction of business layer category object
2 | package category
3 |
4 | import (
5 | "github.com/ottemo/commerce/app/models"
6 | "github.com/ottemo/commerce/app/models/product"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // Package global constants
11 | const (
12 | ConstModelNameCategory = "Category"
13 | ConstModelNameCategoryCollection = "CategoryCollection"
14 |
15 | ConstErrorModule = "category"
16 | ConstErrorLevel = env.ConstErrorLevelModel
17 | )
18 |
19 | // InterfaceCategory represents interface to access business layer implementation of category object
20 | type InterfaceCategory interface {
21 | GetEnabled() bool
22 |
23 | GetName() string
24 |
25 | GetParent() InterfaceCategory
26 |
27 | GetDescription() string
28 |
29 | GetImage() string
30 |
31 | GetProductIds() []string
32 | GetProductsCollection() product.InterfaceProductCollection
33 | GetProducts() []product.InterfaceProduct
34 |
35 | AddProduct(productID string) error
36 | RemoveProduct(productID string) error
37 |
38 | models.InterfaceModel
39 | models.InterfaceObject
40 | models.InterfaceStorable
41 | models.InterfaceMedia
42 | models.InterfaceListable
43 | }
44 |
45 | // InterfaceCategoryCollection represents interface to access business layer implementation of category collection
46 | type InterfaceCategoryCollection interface {
47 | ListCategories() []InterfaceCategory
48 |
49 | models.InterfaceCollection
50 | }
51 |
--------------------------------------------------------------------------------
/app/models/discount/saleprice/decl.go:
--------------------------------------------------------------------------------
1 | package saleprice
2 |
3 | // Public constants
4 | const (
5 | ConstModelNameSalePrice = "SalePrice"
6 | ConstSalePriceDbCollectionName = "sale_prices"
7 | )
8 |
--------------------------------------------------------------------------------
/app/models/discount/saleprice/helpers.go:
--------------------------------------------------------------------------------
1 | package saleprice
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // GetSalePriceModel retrieves current InterfaceSalePrice model implementation
9 | func GetSalePriceModel() (InterfaceSalePrice, error) {
10 | model, err := models.GetModel(ConstModelNameSalePrice)
11 | if err != nil {
12 | return nil, env.ErrorDispatch(err)
13 | }
14 |
15 | salePriceModel, ok := model.(InterfaceSalePrice)
16 | if !ok {
17 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "1ef4f461-e355-4989-9d79-75b060d8153c", "model "+model.GetImplementationName()+" is not 'InterfaceSalePrice' capable")
18 | }
19 |
20 | return salePriceModel, nil
21 | }
22 |
23 | // GetSalePriceCollectionModel retrieves current InterfaceSalePriceCollection model implementation
24 | func GetSalePriceCollectionModel() (InterfaceSalePriceCollection, error) {
25 | model, err := models.GetModel(ConstSalePriceDbCollectionName)
26 | if err != nil {
27 | return nil, env.ErrorDispatch(err)
28 | }
29 |
30 | salePriceCollectionModel, ok := model.(InterfaceSalePriceCollection)
31 | if !ok {
32 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "a6fb5051-626d-4ab7-8e44-5b8b2a1180b5", "model "+model.GetImplementationName()+" is not 'InterfaceSalePriceCollection' capable")
33 | }
34 |
35 | return salePriceCollectionModel, nil
36 | }
37 |
--------------------------------------------------------------------------------
/app/models/discount/saleprice/interfaces.go:
--------------------------------------------------------------------------------
1 | package saleprice
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/ottemo/commerce/app/models"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // Package global constants
11 | const (
12 | ConstErrorModule = "saleprice"
13 | ConstErrorLevel = env.ConstErrorLevelModel
14 | )
15 |
16 | // InterfaceSalePrice represents interface to access business layer implementation of sale price object
17 | type InterfaceSalePrice interface {
18 | GetAmount() float64
19 | SetAmount(float64) error
20 |
21 | GetEndDatetime() time.Time
22 | SetEndDatetime(time.Time) error
23 |
24 | GetProductID() string
25 | SetProductID(string) error
26 |
27 | GetStartDatetime() time.Time
28 | SetStartDatetime(time.Time) error
29 |
30 | models.InterfaceObject
31 | models.InterfaceStorable
32 | }
33 |
34 | // InterfaceSalePriceCollection represents interface to access business layer implementation of sale price collection
35 | type InterfaceSalePriceCollection interface {
36 | ListSalePrices() []InterfaceSalePrice
37 |
38 | models.InterfaceCollection
39 | }
40 |
--------------------------------------------------------------------------------
/app/models/manager.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // RegisterModel registers new model to system
8 | func RegisterModel(ModelName string, Model InterfaceModel) error {
9 | if _, present := declaredModels[ModelName]; present {
10 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "0300eb6b-08b8-497e-afd0-eda0ee358596", "The model with name '"+ModelName+"' has already been registered")
11 | }
12 | declaredModels[ModelName] = Model
13 |
14 | return nil
15 | }
16 |
17 | // UnRegisterModel removes registered model from system
18 | func UnRegisterModel(ModelName string) error {
19 | if _, present := declaredModels[ModelName]; present {
20 | delete(declaredModels, ModelName)
21 | } else {
22 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "3d651c0a-e4a4-443f-a6e8-de3a95d89b5c", "Unable to find model to delete with name '"+ModelName+"'")
23 | }
24 | return nil
25 | }
26 |
27 | // GetModel returns registered in system model
28 | func GetModel(ModelName string) (InterfaceModel, error) {
29 | if model, present := declaredModels[ModelName]; present {
30 | return model.New()
31 | }
32 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "5d49fd0d-1fed-47dc-8e72-2346f1e778c3", "Unable to find model with name '"+ModelName+"'")
33 | }
34 |
35 | // GetDeclaredModels returns all currently registered in system models
36 | func GetDeclaredModels() map[string]InterfaceModel {
37 | return declaredModels
38 | }
39 |
--------------------------------------------------------------------------------
/app/models/product/manager.go:
--------------------------------------------------------------------------------
1 | package product
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | "github.com/ottemo/commerce/app/models/stock"
6 | )
7 |
8 | // Package global variables
9 | var (
10 | registeredStock stock.InterfaceStock
11 | )
12 |
13 | // UnRegisterStock removes stock management from system
14 | func UnRegisterStock() error {
15 | registeredStock = nil
16 | return nil
17 | }
18 |
19 | // RegisterStock registers given stock manager in system
20 | func RegisterStock(stock stock.InterfaceStock) error {
21 | if registeredStock != nil {
22 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "80dc18f2-da63-418e-8430-5a832a4c3bd2", "Already registered")
23 | }
24 | registeredStock = stock
25 |
26 | return nil
27 | }
28 |
29 | // GetRegisteredStock returns currently used stack manager or nil
30 | func GetRegisteredStock() stock.InterfaceStock {
31 | return registeredStock
32 | }
33 |
--------------------------------------------------------------------------------
/app/models/seo/helpers.go:
--------------------------------------------------------------------------------
1 | package seo
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // GetSEO shortcut to registered engine method
9 | func GetSEO(seoType string, objectID string, urlPattern string) []InterfaceSEOItem {
10 | if seoEngine := GetRegisteredSEOEngine(); seoEngine != nil {
11 | return seoEngine.GetSEO(seoType, objectID, urlPattern)
12 | }
13 | return []InterfaceSEOItem{}
14 | }
15 |
16 | // GetSEOItemModel retrieves current InterfaceSEOItem model implementation
17 | func GetSEOItemModel() (InterfaceSEOItem, error) {
18 | model, err := models.GetModel(ConstModelNameSEOItem)
19 | if err != nil {
20 | return nil, env.ErrorDispatch(err)
21 | }
22 |
23 | SEOItemModel, ok := model.(InterfaceSEOItem)
24 | if !ok {
25 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "fa4bce5b-c500-4faf-81ba-9d28cfff72fb", "model "+model.GetImplementationName()+" is not 'InterfaceSEOItem' capable")
26 | }
27 |
28 | return SEOItemModel, nil
29 | }
30 |
31 | // GetSEOItemModelAndSetID retrieves current InterfaceSEOItem model implementation and sets its ID to some value
32 | func GetSEOItemModelAndSetID(SEOItemID string) (InterfaceSEOItem, error) {
33 |
34 | SEOItemModel, err := GetSEOItemModel()
35 | if err != nil {
36 | return nil, env.ErrorDispatch(err)
37 | }
38 |
39 | err = SEOItemModel.SetID(SEOItemID)
40 | if err != nil {
41 | return SEOItemModel, env.ErrorDispatch(err)
42 | }
43 |
44 | return SEOItemModel, nil
45 | }
46 |
47 | // LoadSEOItemByID loads SEOItem data into current InterfaceSEOItem model implementation
48 | func LoadSEOItemByID(SEOItemID string) (InterfaceSEOItem, error) {
49 |
50 | SEOItemModel, err := GetSEOItemModel()
51 | if err != nil {
52 | return nil, env.ErrorDispatch(err)
53 | }
54 |
55 | err = SEOItemModel.Load(SEOItemID)
56 | if err != nil {
57 | return nil, env.ErrorDispatch(err)
58 | }
59 |
60 | return SEOItemModel, nil
61 | }
62 |
--------------------------------------------------------------------------------
/app/models/seo/interfaces.go:
--------------------------------------------------------------------------------
1 | package seo
2 |
3 | import (
4 | "github.com/ottemo/commerce/app/models"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // Package global constants
9 | const (
10 | ConstModelNameSEOItem = "SEOItem"
11 | ConstModelNameSEOItemCollection = "SEOCollection"
12 |
13 | ConstErrorModule = "seo"
14 | ConstErrorLevel = env.ConstErrorLevelModel
15 | )
16 |
17 | // InterfaceSEOEngine represents interface to access business layer implementation of SEO engine
18 | type InterfaceSEOEngine interface {
19 | GetSEO(seoType string, objectID string, urlPattern string) []InterfaceSEOItem
20 | }
21 |
22 | // InterfaceSEOItem represents interface to access business layer implementation of SEO item object
23 | type InterfaceSEOItem interface {
24 | GetURL() string
25 | SetURL(newURL string) error
26 |
27 | GetRewrite() string
28 |
29 | GetTitle() string
30 | GetType() string
31 | GetMetaKeywords() string
32 | GetMetaDescription() string
33 |
34 | models.InterfaceModel
35 | models.InterfaceObject
36 | models.InterfaceStorable
37 | models.InterfaceListable
38 | }
39 |
40 | // InterfaceSEOCollection represents interface to access business layer implementation of SEO items collection
41 | type InterfaceSEOCollection interface {
42 | ListSEOItems() []InterfaceSEOItem
43 |
44 | models.InterfaceCollection
45 | }
46 |
--------------------------------------------------------------------------------
/app/models/stock/helpers.go:
--------------------------------------------------------------------------------
1 | package stock
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 |
6 | "github.com/ottemo/commerce/app/models"
7 | )
8 |
9 | // GetBlogPostModel retrieves current InterfaceBlogPost model implementation
10 | func GetStockModel() (InterfaceStock, error) {
11 | model, err := models.GetModel(ConstModelNameStock)
12 | if err != nil {
13 | return nil, env.ErrorDispatch(err)
14 | }
15 |
16 | stockModel, ok := model.(InterfaceStock)
17 | if !ok {
18 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "d2fd8dc4-094e-4a32-8e89-d8be2f3bf1ea", "model "+model.GetImplementationName()+" is not 'InterfaceStock' capable")
19 | }
20 |
21 | return stockModel, nil
22 | }
23 |
24 | // GetProductCollectionModel retrieves current InterfaceProductCollection model implementation
25 | func GetStockCollectionModel() (InterfaceStockCollection, error) {
26 | model, err := models.GetModel(ConstModelNameStockCollection)
27 | if err != nil {
28 | return nil, env.ErrorDispatch(err)
29 | }
30 |
31 | stockModel, ok := model.(InterfaceStockCollection)
32 | if !ok {
33 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "343218b7-587f-47e5-83a8-a372615116d9", "model "+model.GetImplementationName()+" is not 'InterfaceStockCollection' capable")
34 | }
35 |
36 | return stockModel, nil
37 | }
38 |
--------------------------------------------------------------------------------
/app/models/stock/interfaces.go:
--------------------------------------------------------------------------------
1 | // Package product represents abstraction of business layer product object
2 | package stock
3 |
4 | import (
5 | "github.com/ottemo/commerce/app/models"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // Package global constants
10 | const (
11 | ConstModelNameStock = "Stock"
12 | ConstModelNameStockCollection = "StockCollection"
13 |
14 | ConstErrorModule = "stock"
15 | ConstErrorLevel = env.ConstErrorLevelModel
16 |
17 | //ConstOptionProductIDs = "_ids"
18 | )
19 |
20 | // InterfaceStock represents interface to access business layer implementation of stock management
21 | type InterfaceStock interface {
22 |
23 | GetProductID() string
24 | GetOptions() string
25 | GetQty() int
26 |
27 | SetProductID(product_id string) error
28 | SetOptions(options string) error
29 | SetQty(qty int) error
30 |
31 | SetProductQty(productID string, options map[string]interface{}, qty int) error
32 | GetProductQty(productID string, options map[string]interface{}) int
33 | GetProductOptions(productID string) []map[string]interface{}
34 |
35 | RemoveProductQty(productID string, options map[string]interface{}) error
36 | UpdateProductQty(productID string, options map[string]interface{}, deltaQty int) error
37 |
38 | models.InterfaceModel
39 | models.InterfaceObject
40 | models.InterfaceStorable
41 | models.InterfaceListable
42 | }
43 |
44 | // InterfaceStockCollection represents interface to access business layer implementation of stock collection
45 | type InterfaceStockCollection interface {
46 | ListStocks() []InterfaceStock
47 |
48 | models.InterfaceCollection
49 | }
50 |
--------------------------------------------------------------------------------
/basebuild/build_mongo.go:
--------------------------------------------------------------------------------
1 | // +build mongo
2 |
3 | package basebuild
4 |
5 | import (
6 | // MongoDB based database service
7 | _ "github.com/ottemo/commerce/db/mongo"
8 | )
9 |
--------------------------------------------------------------------------------
/basebuild/build_mysql.go:
--------------------------------------------------------------------------------
1 | // +build mysql
2 |
3 | package basebuild
4 |
5 | import (
6 | // MySQL based database service
7 | _ "github.com/go-sql-driver/mysql"
8 | _ "github.com/ottemo/commerce/db/mysql"
9 | )
10 |
--------------------------------------------------------------------------------
/basebuild/build_postgres.go:
--------------------------------------------------------------------------------
1 | // +build !sqlite,!mysql,!mongo
2 |
3 | package basebuild
4 |
5 | import (
6 | _ "github.com/ottemo/commerce/db/postgres"
7 | )
8 |
--------------------------------------------------------------------------------
/basebuild/build_sqlite.go:
--------------------------------------------------------------------------------
1 | // +build sqlite
2 |
3 | package basebuild
4 |
5 | import (
6 | // SQLite based database service
7 | _ "github.com/ottemo/commerce/db/sqlite"
8 | )
9 |
--------------------------------------------------------------------------------
/basebuild/doc.go:
--------------------------------------------------------------------------------
1 | // Package basebuild represents default Ottemo e-commerce product build.
2 | //
3 | // This package declares an application components to use by void usage import of them. So, these void import packages
4 | // are self-init packages of replaceable modules/extension/plugins.
5 | //
6 | // In order to use this package just use void import:
7 | // import _ "github.com/ottemo/commerce/basebuild"
8 | package basebuild
9 |
--------------------------------------------------------------------------------
/bin/bootstrap.sh:
--------------------------------------------------------------------------------
1 | # TODO: update this old bootstrap script for setting up commerce the first time in /opt dir
2 | apt-get -y install bzr
3 | apt-get -y install git
4 | apt-get -y install golang-go
5 |
6 |
7 | rm -rf /opt/ottemo
8 | mkdir -pv /opt/ottemo/go/src/github.com/ottemo
9 | mkdir -pv /opt/ottemo/go/bin
10 | mkdir -pv /opt/ottemo/go/pkg
11 | mkdir -pv /opt/ottemo/media
12 |
13 | export GOPATH=/opt/ottemo/go
14 |
15 | git clone https://github.com/ottemo/commerce /opt/ottemo/go/src/github.com/ottemo/commerce
16 |
17 | cd $GOPATH/bin
18 | echo "media.fsmedia.folder=/opt/ottemo/media" >> ottemo.ini
19 | echo "mongodb.db=ottemo-demo" >> ottemo.ini
20 | echo "mongodb.uri=mongodb://DB_USER:DB_PASSWROD@MONGO_DB_URI:27017/ottemo" >> ottemo.ini
21 |
22 | cd $GOPATH/src/github.com/ottemo/commerce && go get -t
23 | cd $GOPATH/src/github.com/ottemo/commerce && go get gopkg.in/mgo.v2
24 | cd $GOPATH/src/github.com/ottemo/commerce && go get gopkg.in/mgo.v2/bson
25 | cd $GOPATH/src/github.com/ottemo/commerce && go build -tags mongo
26 |
--------------------------------------------------------------------------------
/bin/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # location of commerce
4 | HOME=/home/ottemo
5 | SRCDIR=/home/ottemo/code/go/src/github.com/ottemo/commerce
6 |
7 | if [ "$BRANCH" == 'develop' ]; then
8 | #GIT_COMMIT=$( echo "$COMMIT" | head -c 5 )
9 | # grab the latest code
10 |
11 | currentBranch=`ssh ottemo@$REMOTE_HOST "cd $SRCDIR && git symbolic-ref --quiet --short HEAD 2> /dev/null || git rev-parse --short HEAD 2> /dev/null || echo '(unknown)'"`
12 | echo ""
13 | echo "COMMERCE BRANCH IS ${currentBranch}"
14 |
15 | if [ "$currentBranch" == 'develop' ]; then
16 | echo "GRAB THE LATEST CODE"
17 | ssh ottemo@$REMOTE_HOST "cd $SRCDIR && git stash && git checkout develop && git fetch --prune && git pull"
18 | fi
19 |
20 | echo ""
21 | echo "BUILD LOCALLY"
22 | ssh ottemo@$REMOTE_HOST "cd $SRCDIR && go get -t ./... && bash bin/make.sh -tags mongo"
23 |
24 | echo "BACKUP THE CURRENT BINARY AND PUT THE NEWLY BUILT BINARY INTO SERVICE"
25 | ssh ottemo@$REMOTE_HOST "sudo service ottemo stop && mv $HOME/commerce/commerce $HOME/commerce/commerce.bak"
26 | ssh ottemo@$REMOTE_HOST "cp $SRCDIR/commerce ~/commerce/commerce && sudo service ottemo start"
27 | fi
28 |
--------------------------------------------------------------------------------
/bin/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "generate commerce config and start it"
4 | echo "you have to define at least MONGOHOST and MONGODB"
5 | echo "MONGOUSER, MONGOPASS, SECURE_COOKIE, SSL_CERT, XDOMAIN, REDIS are optional parametes"
6 |
7 | if ! [ -n "$MONGOHOST" ] ; then
8 | echo "you have to define MONGOHOST environment parameter"
9 | exit 2
10 | fi
11 | if ! [ -n "$MONGODB" ] ; then
12 | echo "you have to define MONGODB environment parameter"
13 | exit 2
14 | fi
15 |
16 | uri="mongodb://"
17 | if [ -n "$MONGOUSER" ] ; then
18 | if [ -n "$MONGOPASS" ] ; then
19 | uri="$uri$MONGOUSER:$MONGOPASS@"
20 | else
21 | uri="$uri$MONGOUSER@"
22 | fi
23 | fi
24 | uri="$uri$MONGOHOST/$MONGODB"
25 |
26 | if [ -n "$MEDIAFOLDER" ] ; then
27 | mkdir -p $MEDIAFOLDER
28 | mkdir -p /home/ottemo/
29 | ln -s $MEDIAFOLDER /home/ottemo/media
30 | echo "media.fsmedia.folder=$MEDIAFOLDER" > ottemo.ini
31 | else
32 | echo "media.fsmedia.folder=/home/ottemo/media" > ottemo.ini
33 | fi
34 |
35 | echo "mongodb.db=$MONGODB" >> ottemo.ini
36 | echo "mongodb.uri=$uri" >> ottemo.ini
37 |
38 | if [ -n "$SECURE_COOKIE" ] ; then
39 | echo "secure_cookie=$SECURE_COOKIE" >> ottemo.ini
40 | else
41 | echo "secure_cookie=false" >> ottemo.ini
42 | fi
43 | # ssl cert can be placed to nfs share or mounted as secret into container on kubernetes
44 | if [ -n "$SSL_CERT" ] ; then
45 | echo "ssl.cert=$SSL_CERT" >> ottemo.ini
46 | fi
47 | if [ -n "$XDOMAIN" ] ; then
48 | echo "xdomain=$XDOMAIN" >> ottemo.ini
49 | fi
50 | if [ -n "$REDISHOST" ] ; then
51 | echo "redis.servers=$REDISHOST" >> ottemo.ini
52 | fi
53 |
54 | echo "use follow ottemo.ini config:"
55 | cat ottemo.ini
56 |
57 | ./commerce
58 |
--------------------------------------------------------------------------------
/bin/mongo-dump-csv.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Use this script to dump all mongodb collections to CSV format for import
4 | # into mysql or sqlite tables. Tables will be dumped into a local folder with
5 | # the same name as DBNAME and dropped into files named after their collection.
6 |
7 | OIFS=$IFS;
8 | IFS=",";
9 |
10 | # fill in your details here
11 | dbname=kg-dev
12 | user=USER
13 | pass=PASSWD
14 | host=candidate.42.mongolayer.com:10243
15 |
16 | # first get all collections in the database
17 | collections=`mongo "$host/$dbname" --username $user --password $pass --eval "rs.slaveOk();db.getCollectionNames();" --quiet`;
18 | collectionArray=($collections);
19 |
20 | # for each collection
21 | for ((i=0; i<${#collectionArray[@]}; ++i));
22 | do
23 | echo 'exporting collection' ${collectionArray[$i]}
24 | # get comma separated list of keys. do this by peeking into the first document in the collection and get his set of keys
25 | keys=`mongo "$host/$dbname" --username $user --password $pass --eval "rs.slaveOk();var keys = []; for(var key in db.${collectionArray[$i]}.find().sort({_id: -1}).limit(1)[0]) { keys.push(key); }; keys;" --quiet`;
26 | # now use mongoexport with the set of keys to export the collection to csv
27 | mongoexport --host $host --username $user --password $pass --quiet --db $dbname --collection ${collectionArray[$i]} --type=csv --fields "$keys" --out $dbname.${collectionArray[$i]}.csv;
28 | done
29 |
30 | IFS=$OIFS;
31 |
--------------------------------------------------------------------------------
/bin/prod_deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # vars for script
4 | PWD=$PWD
5 | SRCDIR=/home/ottemo/code/go/src/github.com/ottemo/commerce
6 |
7 | cd $SRCDIR
8 | # Build commerce
9 | echo "BUILDING COMMERCE"
10 | ./bin/make.sh -tags mongo
11 |
12 | # stop the commerce service
13 | echo "STOP COMMERCE SERVICE"
14 | while sudo service ottemo stop >/dev/null 2>&1; do
15 | echo "warning: commerce is still running"
16 | done
17 | echo "info: commerce has terminated"
18 |
19 | echo "DEPLOYING COMMERCE"
20 | # Backup binaries and restart commerce
21 | cp ~/commerce/commerce ~/commerce/commerce.bak
22 | cp commerce ~/commerce/
23 | sudo service ottemo start
24 |
25 | # restore PWD
26 | cd $PWD
27 |
28 | echo "DEPLOY DONE"
29 |
--------------------------------------------------------------------------------
/bin/update-docs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # location of commerce
4 | SRCDIR=/home/ottemo/code/go/src/github.com/ottemo/commerce
5 |
6 | if [ "$BRANCH" == 'develop' ]; then
7 | GIT_COMMIT=`echo $COMMIT | head -c 5`
8 | # grab the latest code
9 | ssh ottemo@$REMOTE_HOST "cd $SRCDIR && git checkout develop && git fetch --prune && git pull"
10 | # update packate dependencies
11 | ssh ottemo@$REMOTE_HOST "cd $SRCDIR && go get -t ./..."
12 | # restart the documenation service
13 | ssh ottemo@$REMOTE_HOST "sudo service godoc restart"
14 | fi
15 |
--------------------------------------------------------------------------------
/db/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package db represents database services abstraction layer. It provides a set of interfaces and helpers to interact with
5 | database storage services.
6 |
7 | So, when package decides to store something in database it should work with this package instead of trying to interact
8 | with concrete database engine (as Ottemo supposing ability to work with different databases).
9 |
10 | Providing Ottemo with a new database engine supposes implementation of "InterfaceDBEngine" with following registration
11 | for db package.
12 |
13 | For database type specification you should refer this package for possible types.
14 |
15 | Example:
16 | --------
17 | collection, err := db.GetCollection( myCollectionName )
18 | if err != nil {
19 | return env.ErrorDispatch(err)
20 | }
21 |
22 | collection.AddColumn("customer_id", db.ConstTypeID, false)
23 | collection.AddColumn("customer_email", db.TypeWPrecision(db.ConstTypeVarchar, 100), true)
24 | collection.AddColumn("bonus_code", db.ConstTypeInteger, false)
25 | collection.AddColumn("bonus_amount", db.ConstTypeInteger, false)
26 |
27 | */
28 | package db
29 |
--------------------------------------------------------------------------------
/db/manager.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // Package global variables
8 | var (
9 | currentDBEngine InterfaceDBEngine // currently registered database service in system
10 | callbacksOnDatabaseStart = []func() error{} // set of callback function on database service start
11 | )
12 |
13 | // RegisterOnDatabaseStart registers new callback on database service start
14 | func RegisterOnDatabaseStart(callback func() error) {
15 | callbacksOnDatabaseStart = append(callbacksOnDatabaseStart, callback)
16 | }
17 |
18 | // OnDatabaseStart fires database service start event (callback handling)
19 | func OnDatabaseStart() error {
20 | for _, callback := range callbacksOnDatabaseStart {
21 | if err := callback(); err != nil {
22 | return env.ErrorDispatch(err)
23 | }
24 | }
25 | return nil
26 | }
27 |
28 | // RegisterDBEngine registers database service in the system
29 | // - will cause error if there are couple candidates for that role
30 | func RegisterDBEngine(newEngine InterfaceDBEngine) error {
31 | if currentDBEngine == nil {
32 | currentDBEngine = newEngine
33 | } else {
34 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "c8588998-a99b-406a-91cb-cb5766e34f7e", "Sorry, '"+currentDBEngine.GetName()+"' already registered")
35 | }
36 | return nil
37 | }
38 |
39 | // GetDBEngine returns currently used database service implementation
40 | func GetDBEngine() InterfaceDBEngine {
41 | return currentDBEngine
42 | }
43 |
--------------------------------------------------------------------------------
/db/mongo/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package mongo is a default mongoDB implementation for Ottemo. It provides "InterfaceDBEngine" implementation declared in
5 | "github.com/ottemo/commerce/db" package.
6 |
7 | Package stands on a top of "gopkg.in/mgo.v2" package.
8 |
9 | MongoDB is the only database that harnesses the innovations of NoSQL (flexibility, scalability, performance) and builds
10 | on the commerce of relational databases (expressive query language, secondary indexes, strong consistency).
11 | */
12 | package mongo
13 |
--------------------------------------------------------------------------------
/db/mongo/init.go:
--------------------------------------------------------------------------------
1 | package mongo
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // init makes package self-initialization routine
9 | func init() {
10 | instance := new(DBEngine)
11 |
12 | var dbConnector = db.NewDBConnector(instance)
13 | env.RegisterOnConfigIniStart(dbConnector.ConnectAsync)
14 |
15 | _ = db.RegisterDBEngine(instance)
16 | }
17 |
18 | // Output is a implementation of mgo.log_Logger interface
19 | func (it *DBEngine) Output(calldepth int, s string) error {
20 | env.Log("mongo.log", "DEBUG", s)
21 | return nil
22 | }
23 |
--------------------------------------------------------------------------------
/db/mongo/utils.go:
--------------------------------------------------------------------------------
1 | package mongo
2 |
3 | import (
4 | "gopkg.in/mgo.v2/bson"
5 | "sort"
6 | "time"
7 |
8 | "github.com/ottemo/commerce/utils"
9 | )
10 |
11 | // ConvertMapToDoc is a recursive function that converts map[string]interface{} to bson.D
12 | // so map keys order is static and alphabetically sorted
13 | func ConvertMapToDoc(inputMap map[string]interface{}) bson.D {
14 | result := make(bson.D, len(inputMap))
15 |
16 | // making sorted array of map keys
17 | //--------------------------------
18 | sortedKeys := make([]string, len(inputMap))
19 | var idx = 0
20 | for key := range inputMap {
21 | sortedKeys[idx] = key
22 | idx++
23 | }
24 | sort.Strings(sortedKeys)
25 |
26 | // converting key values to bson.DocElem
27 | for _, key := range sortedKeys {
28 | var docValue interface{} = inputMap[key]
29 | if mapValue, ok := docValue.(map[string]interface{}); ok {
30 | docValue = ConvertMapToDoc(mapValue)
31 | }
32 | result = append(result, bson.DocElem{Name: key, Value: docValue})
33 | }
34 |
35 | return result
36 | }
37 |
38 | // BsonDToString converts bson.D to readable form, mostly used for debug
39 | func BsonDToString(input bson.D) string {
40 | result := ""
41 |
42 | result += "{"
43 | for _, bsonItem := range input {
44 | result += "'" + bsonItem.Name + "': "
45 |
46 | switch typedValue := bsonItem.Value.(type) {
47 | case []bson.D:
48 | result += "["
49 |
50 | addComaFlag := false
51 | for _, valueItem := range typedValue {
52 | if addComaFlag {
53 | result += ", "
54 | } else {
55 | addComaFlag = true
56 | }
57 | result += BsonDToString(valueItem)
58 | }
59 |
60 | result += "]"
61 | case bson.D:
62 | result += BsonDToString(typedValue)
63 | case time.Time:
64 | result += "ISODate(\"" + typedValue.Format(time.RFC3339) + "\")"
65 | default:
66 | if bsonItem.Value == nil {
67 | result += "null"
68 | }
69 | result += utils.InterfaceToString(typedValue)
70 | }
71 | }
72 | result += "}"
73 |
74 | return result
75 | }
76 |
--------------------------------------------------------------------------------
/db/mysql/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package mysql is a default MySQL implementation for Ottemo. It provides "InterfaceDBEngine" implementation declared in
5 | "github.com/ottemo/commerce/db" package.
6 |
7 | Package stands on a top of "github.com/go-sql-driver/mysql" package.
8 |
9 | */
10 | package mysql
11 |
--------------------------------------------------------------------------------
/db/mysql/init.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // init makes package self-initialization routine
9 | func init() {
10 | dbEngine = new(DBEngine)
11 | dbEngine.attributeTypes = make(map[string]map[string]string)
12 |
13 | var _ db.InterfaceDBEngine = dbEngine
14 |
15 | var dbConnector = db.NewDBConnector(dbEngine)
16 | env.RegisterOnConfigIniStart(dbConnector.ConnectAsync)
17 |
18 | if err := db.RegisterDBEngine(dbEngine); err != nil {
19 | _ = env.ErrorDispatch(err)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/db/postgres/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The Ottemo Authors. All rights reserved.
2 |
3 | /*
4 | Package postgres is a default Postgres implementation for Ottemo. It provides "InterfaceDBEngine" implementation declared in
5 | "github.com/ottemo/commerce/db" package.
6 |
7 | Package stands on a top of "github.com/go-sql-driver/postgres" package.
8 |
9 | */
10 | package postgres
11 |
--------------------------------------------------------------------------------
/db/postgres/init.go:
--------------------------------------------------------------------------------
1 | package postgres
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | "github.com/ottemo/commerce/env"
6 |
7 | // Postgres driver for the database/sql package
8 | _ "github.com/lib/pq"
9 | )
10 |
11 | // init makes package self-initialization routine
12 | func init() {
13 | dbEngine = new(DBEngine)
14 | dbEngine.attributeTypes = make(map[string]map[string]string)
15 |
16 | var _ db.InterfaceDBEngine = dbEngine
17 |
18 | var dbConnector = db.NewDBConnector(dbEngine)
19 | env.RegisterOnConfigIniStart(dbConnector.ConnectAsync)
20 |
21 | if err := db.RegisterDBEngine(dbEngine); err != nil {
22 | _ = env.ErrorDispatch(err)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/db/postgres/postgres_test.go:
--------------------------------------------------------------------------------
1 | package postgres
2 |
3 | import (
4 | "testing"
5 | "github.com/ottemo/commerce/db"
6 | _ "github.com/lib/pq"
7 | )
8 |
9 | func TestSimple(t *testing.T) {
10 | var dbConnector = db.NewDBConnector(dbEngine)
11 | if err := dbConnector.Connect(); err != nil {
12 | t.Fatal(err)
13 | }
14 | db.TestSimple(t)
15 | }
16 |
--------------------------------------------------------------------------------
/db/sqlite/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package sqlite is a default SQLite implementation for Ottemo. It provides "InterfaceDBEngine" implementation declared in
5 | "github.com/ottemo/commerce/db" package.
6 |
7 | Package stands on a top of "github.com/mxk/go-sqlite/sqlite3" package.
8 |
9 | SQLite is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database
10 | engine. SQLite is the most widely deployed SQL database engine in the world. The source code for SQLite is in the public
11 | domain.
12 | */
13 | package sqlite
14 |
--------------------------------------------------------------------------------
/db/sqlite/init.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "github.com/ottemo/commerce/db"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // init makes package self-initialization routine
9 | func init() {
10 | dbEngine = new(DBEngine)
11 | dbEngine.attributeTypes = make(map[string]map[string]string)
12 |
13 | var _ db.InterfaceDBEngine = dbEngine
14 |
15 | var dbConnector = db.NewDBConnector(dbEngine)
16 | env.RegisterOnConfigIniStart(dbConnector.ConnectAsync)
17 |
18 | if err := db.RegisterDBEngine(dbEngine); err != nil {
19 | _ = env.ErrorDispatch(err)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/env/config/decl.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // Package global constants
8 | const (
9 | ConstCollectionNameConfig = "config"
10 |
11 | ConstErrorModule = "env/config"
12 | ConstErrorLevel = env.ConstErrorLevelService
13 | )
14 |
15 | // DefaultConfig is a default implementer of InterfaceConfig
16 | type DefaultConfig struct {
17 | configValues map[string]interface{}
18 | configTypes map[string]string
19 | configValidators map[string]env.FuncConfigValueValidator
20 | }
21 |
--------------------------------------------------------------------------------
/env/config/i_impex_model.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/ottemo/commerce/utils"
5 | )
6 |
7 | // Import imports value in config through Impex
8 | func (it *DefaultConfig) Import(item map[string]interface{}, testMode bool) (map[string]interface{}, error) {
9 | var path string
10 | var value interface{}
11 |
12 | if pathValue, present := item["path"]; present {
13 | path = utils.InterfaceToString(pathValue)
14 | } else if keyValue, present := item["key"]; present {
15 | path = utils.InterfaceToString(keyValue)
16 | }
17 |
18 | value, _ = item["value"]
19 |
20 | if testMode == false {
21 | err := it.SetValue(path, value)
22 | return item, err
23 | }
24 |
25 | return item, nil
26 | }
27 |
28 | // Export exports config values through Impex
29 | func (it *DefaultConfig) Export(iterator func(map[string]interface{}) bool) error {
30 | for itemPath, itemValue := range it.configValues {
31 | continueFlag := iterator(map[string]interface{}{"path": itemPath, "value": itemValue})
32 | if continueFlag == false {
33 | break
34 | }
35 | }
36 |
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/env/config/init.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/db"
6 | "github.com/ottemo/commerce/env"
7 | "github.com/ottemo/commerce/impex"
8 | )
9 |
10 | // init makes package self-initialization routine
11 | func init() {
12 | instance := &DefaultConfig{
13 | configValues: make(map[string]interface{}),
14 | configTypes: make(map[string]string),
15 | configValidators: make(map[string]env.FuncConfigValueValidator)}
16 |
17 | db.RegisterOnDatabaseStart(setupDB)
18 | db.RegisterOnDatabaseStart(instance.Load)
19 |
20 | api.RegisterOnRestServiceStart(setupAPI)
21 |
22 | if err := env.RegisterConfig(instance); err != nil {
23 | _ = env.ErrorDispatch(err)
24 | }
25 |
26 | if err := impex.RegisterImpexModel("Config", instance); err != nil {
27 | _ = env.ErrorDispatch(err)
28 | }
29 | }
30 |
31 | // setupDB prepares system database for package usage
32 | func setupDB() error {
33 | collection, err := db.GetCollection(ConstCollectionNameConfig)
34 | if err != nil {
35 | return env.ErrorDispatch(err)
36 | }
37 |
38 | if err := collection.AddColumn("path", db.ConstTypeVarchar, true); err != nil {
39 | return env.ErrorDispatch(err)
40 | }
41 | if err := collection.AddColumn("value", db.ConstTypeText, false); err != nil {
42 | return env.ErrorDispatch(err)
43 | }
44 |
45 | if err := collection.AddColumn("type", db.ConstTypeVarchar, false); err != nil {
46 | return env.ErrorDispatch(err)
47 | }
48 |
49 | if err := collection.AddColumn("editor", db.ConstTypeVarchar, false); err != nil {
50 | return env.ErrorDispatch(err)
51 | }
52 | if err := collection.AddColumn("options", db.ConstTypeText, false); err != nil {
53 | return env.ErrorDispatch(err)
54 | }
55 |
56 | if err := collection.AddColumn("label", db.ConstTypeVarchar, false); err != nil {
57 | return env.ErrorDispatch(err)
58 | }
59 | if err := collection.AddColumn("description", db.ConstTypeText, false); err != nil {
60 | return env.ErrorDispatch(err)
61 | }
62 |
63 | if err := collection.AddColumn("image", db.ConstTypeVarchar, false); err != nil {
64 | return env.ErrorDispatch(err)
65 | }
66 |
67 | return nil
68 | }
69 |
--------------------------------------------------------------------------------
/env/cron/decl.go:
--------------------------------------------------------------------------------
1 | package cron
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/gorhill/cronexpr"
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // Package global constants
11 | const (
12 | ConstErrorModule = "env/cron"
13 | ConstErrorLevel = env.ConstErrorLevelService
14 | )
15 |
16 | // DefaultCronScheduler is a default implementer of InterfaceIniConfig
17 | type DefaultCronScheduler struct {
18 | tasks map[string]env.FuncCronTask
19 | schedules []*DefaultCronSchedule
20 |
21 | appStarted bool
22 | }
23 |
24 | // DefaultCronSchedule structure to hold schedule information (for internal usage)
25 | type DefaultCronSchedule struct {
26 | CronExpr string
27 | TaskName string
28 | Params map[string]interface{}
29 | Repeat bool
30 | Time time.Time
31 | active bool
32 |
33 | task env.FuncCronTask
34 | expr *cronexpr.Expression
35 |
36 | scheduler *DefaultCronScheduler
37 | }
38 |
--------------------------------------------------------------------------------
/env/cron/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 Ottemo, All rights reserved.
2 |
3 | /*
4 | Package cron is a utility to schedule tasks. These tasks maybe scheduled for
5 | a specific time, they may be repeatable or intended to be run immediately.
6 | There are several API Endpoints which allow a developer to interact with the
7 | scheduling system.
8 |
9 | It is important to understand several concepts.
10 |
11 | Task - a job which can be scheduled to run at a specific time
12 | Schedule - a listing of all active tasks, when they will be executed and their respective metadata
13 |
14 | The API allows you to:
15 | * Obtain a list of the currently scheduled tasks
16 | * Create a task to be run on a schedule
17 | * Obtain a list of possible tasks to be scheduled
18 | * Enable a task to be run on a schedule
19 | * Disable a task
20 | * Update the specified task
21 | * Run the specified task now
22 |
23 | //TODO: add link to api documentation
24 |
25 | */
26 | package cron
27 |
--------------------------------------------------------------------------------
/env/cron/init.go:
--------------------------------------------------------------------------------
1 | package cron
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/app"
6 | "github.com/ottemo/commerce/env"
7 | )
8 |
9 | // init makes package self-initialization routine
10 | func init() {
11 | instance := new(DefaultCronScheduler)
12 | var _ env.InterfaceScheduler = instance
13 | var _ env.InterfaceSchedule = new(DefaultCronSchedule)
14 |
15 | instance.tasks = make(map[string]env.FuncCronTask)
16 | instance.schedules = make([]*DefaultCronSchedule, 0)
17 |
18 | app.OnAppInit(instance.appInitEvent)
19 | app.OnAppEnd(instance.appEndEvent)
20 | api.RegisterOnRestServiceStart(setupAPI)
21 |
22 | if err := env.RegisterScheduler(instance); err != nil {
23 | _ = env.ErrorDispatch(err)
24 | }
25 | }
26 |
27 | // routines before application end
28 | func (it *DefaultCronScheduler) appEndEvent() error {
29 | return nil
30 | }
31 |
32 | // routines before application start (on init phase)
33 | func (it *DefaultCronScheduler) appInitEvent() error {
34 |
35 | // TODO: load manually specified tasks from DB
36 |
37 | it.appStarted = true
38 |
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/env/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package env represents environment services abstraction layer. It provides a set of interfaces and helpers to make
5 | application input/output operations and messaging behind scenes.
6 |
7 | For a meantime package consists of:
8 | - configuration service
9 | - ini file service
10 | - error bus
11 | - event bus
12 | - logger
13 | */
14 | package env
15 |
--------------------------------------------------------------------------------
/env/errorbus/decl.go:
--------------------------------------------------------------------------------
1 | package errorbus
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | "regexp"
6 | )
7 |
8 | // Package global constants
9 | const (
10 | ConstCollectCallStack = true // flag to indicate that call stack information within error is required
11 |
12 | ConstConfigPathError = "general.error"
13 | ConstConfigPathErrorHideLevel = "general.error.hide_level"
14 | ConstConfigPathErrorHideMessage = "general.error.hide_message"
15 |
16 | ConstErrorModule = "env/errorbus"
17 | ConstErrorLevel = env.ConstErrorLevelService
18 | )
19 |
20 | // Package global variables
21 | var (
22 | // ConstMsgRegexp is a regular expression used to parse error message
23 | ConstMsgRegexp = regexp.MustCompile(`^[\[{(]?\s*(?:(?:([a-zA-Z_\/-]+)?[:])?([0-9]+)?[-: ]([0-9a-fA-F-]+)?)?\s*[\]})]?\s*[:\->]*\s*(.+)`)
24 |
25 | debug = true
26 | hideLevel = 5
27 | hideMessage = "System error has occured"
28 | )
29 |
30 | // DefaultErrorBus InterfaceErrorBus implementer class
31 | type DefaultErrorBus struct {
32 | listeners []env.FuncErrorListener
33 | }
34 |
35 | // OttemoError @reconcile@ InterfaceOttemoError implementer class
36 | type OttemoError struct {
37 | Message string
38 | Module string
39 | Code string
40 | Level int
41 |
42 | CallStack string
43 |
44 | handled bool
45 | logged bool
46 | }
47 |
--------------------------------------------------------------------------------
/env/errorbus/i_ottemo_error.go:
--------------------------------------------------------------------------------
1 | package errorbus
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // returns error message only
8 | func (it *OttemoError) Error() string {
9 | if it.Level < hideLevel {
10 | return hideMessage
11 | }
12 | return it.Message
13 | }
14 |
15 | // ErrorMessage returns original error message
16 | func (it *OttemoError) ErrorMessage() string {
17 | return it.Message
18 | }
19 |
20 | // ErrorFull returns error detail information about error
21 | func (it *OttemoError) ErrorFull() string {
22 | message := it.Message
23 | if it.CallStack != "" {
24 | message += "\n" + it.CallStack
25 | }
26 |
27 | module := it.Module
28 | if module != "" {
29 | module += ":"
30 | }
31 | return fmt.Sprintf("%s%d:%s - %s", module, it.Level, it.Code, message)
32 | }
33 |
34 | // ErrorLevel returns error level - if specified or 0
35 | func (it *OttemoError) ErrorLevel() int {
36 | return it.Level
37 | }
38 |
39 | // ErrorCode returns error code (hexadecimal value) if specified, otherwise MD5 over error message
40 | func (it *OttemoError) ErrorCode() string {
41 | return it.Code
42 | }
43 |
44 | // ErrorCallStack returns error functions call stack for error
45 | // Note: ConstCollectStack constant should be set to true, otherwise, stack information will be blank
46 | func (it *OttemoError) ErrorCallStack() string {
47 | return it.CallStack
48 | }
49 |
50 | // IsHandled returns handled flag
51 | func (it *OttemoError) IsHandled() bool {
52 | return it.handled
53 | }
54 |
55 | // MarkHandled makes error as already processed (prevents from future processing)
56 | func (it *OttemoError) MarkHandled() bool {
57 | it.handled = true
58 | return it.handled
59 | }
60 |
61 | // IsLogged returns logged flag
62 | func (it *OttemoError) IsLogged() bool {
63 | return it.logged
64 | }
65 |
66 | // MarkLogged makes error as already logged (prevents from future logging)
67 | func (it *OttemoError) MarkLogged() bool {
68 | it.logged = true
69 | return it.logged
70 | }
71 |
--------------------------------------------------------------------------------
/env/errorbus/init.go:
--------------------------------------------------------------------------------
1 | package errorbus
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | "github.com/ottemo/commerce/utils"
6 | )
7 |
8 | // init makes package self-initialization routine
9 | func init() {
10 | instance := &DefaultErrorBus{listeners: make([]env.FuncErrorListener, 0)}
11 | var _ env.InterfaceErrorBus = instance
12 |
13 | var _ env.InterfaceOttemoError = new(OttemoError)
14 |
15 | if err := env.RegisterErrorBus(instance); err != nil {
16 | _ = env.ErrorDispatch(err)
17 | }
18 | env.RegisterOnConfigIniStart(setupOnIniConfigStart)
19 | env.RegisterOnConfigStart(setupConfig)
20 | }
21 |
22 | // setupOnIniConfigStart is a initialization based on ini config service
23 | func setupOnIniConfigStart() error {
24 |
25 | if iniConfig := env.GetIniConfig(); iniConfig != nil {
26 | if iniValue := iniConfig.GetValue("error.instant.debug", "true"); iniValue != "" {
27 | debug = utils.InterfaceToBool(iniValue)
28 | }
29 | }
30 |
31 | return nil
32 | }
33 |
--------------------------------------------------------------------------------
/env/eventbus/decl.go:
--------------------------------------------------------------------------------
1 | package eventbus
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // DefaultEventBus InterfaceEventBus implementer class
8 | type DefaultEventBus struct {
9 | listeners map[string][]env.FuncEventListener
10 | }
11 |
--------------------------------------------------------------------------------
/env/eventbus/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package eventbus is a default implementation of InterfaceEventBus declared in "github.com/ottemo/commerce/env" package.
5 |
6 | Event bus is a service used for simplified communication between application code. Event provider emits an event message
7 | and event listeners makes special handling for an event.
8 |
9 | Event name is "." delimited string. So, even listeners can listen for all messages of "top level" message (i.e. listener
10 | for "api" event will listen for "api.checkout.visitCheckout" automatically).
11 |
12 | Event provides a data objects relative to. These objects could be changed during event handling, as well as new data
13 | could be added to a data map, during event processing.
14 |
15 | To be more consistent and clear, event names should be declared as a package constants with description about providing
16 | event data map.
17 |
18 | Example 1:
19 | ----------
20 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "004e9f7b-bb97-4356-bbc2-5e084736983b", "unknown cmd '"+args[0]+"'")
21 | env.Event("api.checkout.visitCheckout", eventData)
22 |
23 | Example 2:
24 | ----------
25 | salesHandler := func(event string, eventData map[string]interface{}) bool {
26 | env.LogMessage( fmt.Sprintf("%+v", eventData) )
27 | }
28 | env.EventRegisterListener("checkout.success", salesHandler)
29 | */
30 | package eventbus
31 |
--------------------------------------------------------------------------------
/env/eventbus/i_event_bus.go:
--------------------------------------------------------------------------------
1 | package eventbus
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // RegisterListener adds listener to event handling stack
8 | // - event listening is patch based, "" - global listener on any event, "api.product" - will listen for app events starts with api.product.[...])
9 | func (it *DefaultEventBus) RegisterListener(event string, listener env.FuncEventListener) {
10 | if value, present := it.listeners[event]; present {
11 | it.listeners[event] = append(value, listener)
12 | } else {
13 | it.listeners[event] = []env.FuncEventListener{listener}
14 | }
15 | }
16 |
17 | // New generates new event, with following dispatching
18 | func (it *DefaultEventBus) New(event string, args map[string]interface{}) {
19 |
20 | // loop over top level events
21 | // (i.e. "api.checkout.success" event will notify following listeners: "", "api", "api.checkout", "api.checkout.success")
22 | lastChar := len(event) - 1
23 | for charIdx, char := range event {
24 | if charIdx == 0 || charIdx == lastChar || char == '.' {
25 | levelEvent := event
26 | if charIdx != lastChar {
27 | levelEvent = event[0:charIdx]
28 | }
29 |
30 | // processing listeners withing level if present
31 | if listeners, present := it.listeners[levelEvent]; present {
32 | for _, listener := range listeners {
33 |
34 | // processing listener, if it wants to stop handling - doing this
35 | if listener(event, args) == false {
36 | return
37 | }
38 |
39 | }
40 | }
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/env/eventbus/init.go:
--------------------------------------------------------------------------------
1 | package eventbus
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // init makes package self-initialization routine
8 | func init() {
9 | instance := new(DefaultEventBus)
10 | instance.listeners = make(map[string][]env.FuncEventListener)
11 |
12 | var _ env.InterfaceEventBus = instance
13 |
14 | if err := env.RegisterEventBus(instance); err != nil {
15 | _ = env.ErrorDispatch(err)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/env/ini/decl.go:
--------------------------------------------------------------------------------
1 | package ini
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // Package global constants
8 | const (
9 | ConstIniGlobalSection = "" // ini file section name to be used as default section
10 | ConstAskForValuePrefix = "?" // prefix used before default ini value to be asked in console if not set
11 |
12 | ConstCmdArgStoreAllFlag = "--iniStoreAll"
13 | ConstCmdArgSectionName = "--iniSection="
14 | ConstCmdArgTestFlag = "--test"
15 |
16 | ConstEnvironmentIniFile = "OTTEMO_INI"
17 | ConstEnvironmentIniSection = "OTTEMO_MODE"
18 |
19 | ConstTestSectionName = "test"
20 | ConstDefaultIniFile = "ottemo.ini"
21 | ConstBackupFileSuffix = ".bak"
22 |
23 | ConstErrorModule = "env/ini"
24 | ConstErrorLevel = env.ConstErrorLevelService
25 | )
26 |
27 | // DefaultIniConfig is a default implementer of InterfaceIniConfig
28 | type DefaultIniConfig struct {
29 | iniFilePath string
30 |
31 | iniFileValues map[string]map[string]string
32 | currentSection string
33 |
34 | keysToStore map[string]bool
35 | storeAll bool
36 | }
37 |
--------------------------------------------------------------------------------
/env/ini/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package ini is a default implementation of InterfaceIniConfig declared in "github.com/ottemo/commerce/env" package.
5 |
6 | Ini config is a config.ini file where startup information located. Ini file could be separated on a sections (refer to
7 | https://github.com/vaughan0/go-ini) for ini file lookup. Ini config takes a value from ini file "current" section and
8 | if not found in there - looking for a global section.
9 |
10 | Special section [ConstTestSectionName] ("test") used for "test mode" application startup. To start application in that
11 | mode [ConstCmdArgTestFlag] "--test" should be used.
12 |
13 | Example 1:
14 | ----------
15 | if iniConfig := env.GetIniConfig(); iniConfig != nil {
16 | if iniValue := iniConfig.GetValue("db.sqlite3.uri", uri); iniValue != "" {
17 | uri = iniValue
18 | }
19 | }
20 |
21 | Example 2:
22 | ----------
23 | uri := env.IniValue("db.sqlite3.uri")
24 | */
25 | package ini
26 |
--------------------------------------------------------------------------------
/env/logger/config.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "errors"
5 | "github.com/ottemo/commerce/env"
6 | "github.com/ottemo/commerce/utils"
7 | )
8 |
9 | // setupConfig setups package configuration values for a system
10 | func setupConfig() error {
11 | config := env.GetConfig()
12 | if config == nil {
13 | err := env.ErrorNew(ConstErrorModule, env.ConstErrorLevelStartStop, "6dee39ac-c930-420e-b777-b95f6cab8981", "can't obtain config")
14 | return env.ErrorDispatch(err)
15 | }
16 |
17 | err := config.RegisterItem(env.StructConfigItem{
18 | Path: ConstConfigPathError,
19 | Value: nil,
20 | Type: env.ConstConfigTypeGroup,
21 | Editor: "",
22 | Options: nil,
23 | Label: "Error",
24 | Description: "error handling settings",
25 | Image: "",
26 | }, nil)
27 |
28 | if err != nil {
29 | return env.ErrorDispatch(err)
30 | }
31 |
32 | // Log level
33 | logLevelValidator := func(newValue interface{}) (interface{}, error) {
34 | newLevel := utils.InterfaceToInt(newValue)
35 | if newLevel > 10 || newLevel < 0 {
36 | err := errors.New("'Log level' config value should be between 0 and 10")
37 | return errorLogLevel, env.ErrorDispatch(err)
38 | }
39 | errorLogLevel = newLevel
40 |
41 | return errorLogLevel, nil
42 | }
43 | err = config.RegisterItem(env.StructConfigItem{
44 | Path: ConstConfigPathErrorLogLevel,
45 | Value: 5,
46 | Type: env.ConstConfigTypeInteger,
47 | Editor: "integer",
48 | Options: nil,
49 | Label: "Log level",
50 | Description: "errors below specified level will be send to logger service",
51 | Image: "",
52 | }, logLevelValidator)
53 |
54 | if err != nil {
55 | return env.ErrorDispatch(err)
56 | }
57 |
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/env/logger/decl.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import "github.com/ottemo/commerce/env"
4 |
5 | // Package global constants
6 | const (
7 | ConstCollectCallStack = true // flag to indicate that call stack information within error is required
8 |
9 | ConstConfigPathError = "general.error"
10 | ConstConfigPathErrorLogLevel = "general.error.log_level"
11 |
12 | ConstErrorModule = "env/logger"
13 | ConstErrorLevel = env.ConstErrorLevelService
14 | )
15 |
16 | // Package global variables
17 | var (
18 | baseDirectory = "./var/log/" // folder location where to store logs
19 | defaultLogFile = "system.log" // filename for default log
20 | defaultErrorsFile = "errors.log" // filename for errors log
21 |
22 | errorLogLevel = 5
23 | )
24 |
25 | // DefaultLogger is a default implementer of InterfaceLogger
26 | type DefaultLogger struct{}
27 |
--------------------------------------------------------------------------------
/env/logger/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package logger is a default implementation of InterfaceLogger declared in "github.com/ottemo/commerce/env" package.
5 |
6 | Default logger is pretty simple implementation, which takes an message and puts it into a "storage" (a file with specific
7 | name in this case). If for some reason message can not be places in file (file access denied, etc.) message will be
8 | printed to stdout. Message time (in RFC3339 format) and specified prefix adds to message before output.
9 | */
10 | package logger
11 |
--------------------------------------------------------------------------------
/env/logger/init.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "os"
5 | "fmt"
6 |
7 | "github.com/ottemo/commerce/env"
8 | )
9 |
10 | // init makes package self-initialization routine
11 | func init() {
12 | instance := new(DefaultLogger)
13 | var _ env.InterfaceLogger = instance
14 |
15 | if err := env.RegisterLogger(instance); err != nil {
16 | fmt.Println(err.Error())
17 | }
18 | env.RegisterOnConfigIniStart(startup)
19 | env.RegisterOnConfigStart(setupConfig)
20 | }
21 |
22 | // startup is a service pre-initialization stuff
23 | func startup() error {
24 | if _, err := os.Stat(baseDirectory); !os.IsExist(err) {
25 | err := os.MkdirAll(baseDirectory, os.ModePerm)
26 | if err != nil {
27 | return err
28 | }
29 | }
30 |
31 | return nil
32 | }
33 |
--------------------------------------------------------------------------------
/foundation.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name commerce.dev.ottemo.io;
4 |
5 | location / {
6 | proxy_pass http://127.0.0.1:3000
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/impex/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package impex represents import/export service. It provides a set of interfaces and helpers for extending it's functionality
5 | as well as implements basic ideas of import/export functionality.
6 |
7 | Importing and exporting happens through CSV files you should specify within API request in order to ue this service. CSV file
8 | converts into map[string]interface{} objects which applies to model instances.
9 |
10 | Refer to "Ottemo Import/Export Manual" for detail on service usage.
11 | */
12 | package impex
13 |
--------------------------------------------------------------------------------
/impex/helpers.go:
--------------------------------------------------------------------------------
1 | package impex
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/env"
6 | )
7 |
8 | // ImportStartHandler is a middleware to init importState for async "next" procedure.
9 | func ImportStartHandler(next api.FuncAPIHandler) api.FuncAPIHandler {
10 | return func(context api.InterfaceApplicationContext) (interface{}, error) {
11 | if importStatus.state != constImportStateIdle {
12 | additionalMessage := ""
13 | if importStatus.file != nil {
14 | additionalMessage = " Currently processing " + importStatus.file.name
15 | }
16 | return nil, env.ErrorNew(ConstErrorModule, env.ConstErrorLevelAPI, "4bec46b6-b6b0-4821-8978-44d0f051750d", "Another import is in progres." + additionalMessage)
17 | }
18 |
19 | importStatus.state = constImportStateProcessing
20 | delete(importStatus.sessions, context.GetSession().GetID())
21 | return next(context)
22 | }
23 | }
24 |
25 | // ImportResultHandler will process import call's result
26 | // It return no values, because of async handler result processing.
27 | var ImportResultHandler = func(context api.InterfaceApplicationContext, result interface{}, err error) {
28 | importStatus.state = constImportStateIdle
29 |
30 | importStatus.sessions[context.GetSession().GetID()] = map[string]interface{}{
31 | "result": result,
32 | "err": err,
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/impex/interfaces.go:
--------------------------------------------------------------------------------
1 | package impex
2 |
3 | // InterfaceImpexImportCmd is an interface used to work with registered import commands
4 | type InterfaceImpexImportCmd interface {
5 | Init(args []string, exchange map[string]interface{}) error
6 | Process(itemData map[string]interface{}, input interface{}, exchange map[string]interface{}) (interface{}, error)
7 | Test(itemData map[string]interface{}, input interface{}, exchange map[string]interface{}) (interface{}, error)
8 | }
9 |
10 | // InterfaceImpexModel is an interface model should implement to make it work with impex service
11 | type InterfaceImpexModel interface {
12 | Import(item map[string]interface{}, testMode bool) (map[string]interface{}, error)
13 | Export(func(map[string]interface{}) bool) error
14 | }
15 |
--------------------------------------------------------------------------------
/impex/manager.go:
--------------------------------------------------------------------------------
1 | package impex
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // RegisterImportCommand registers new command to import/export system
8 | func RegisterImportCommand(commandName string, command InterfaceImpexImportCmd) error {
9 | if _, present := importCmd[commandName]; present {
10 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "f4f50cfc-3de6-4c76-8518-02ad26790f44", commandName+" already registered in impex")
11 | }
12 |
13 | importCmd[commandName] = command
14 |
15 | return nil
16 | }
17 |
18 | // UnRegisterImportCommand un-registers command from import/export system
19 | func UnRegisterImportCommand(commandName string) error {
20 | if _, present := importCmd[commandName]; !present {
21 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "a9df49e3-0d06-4afa-8e3a-7cde5a3b2a1d", "can't find registered command "+commandName)
22 | }
23 |
24 | delete(importCmd, commandName)
25 |
26 | return nil
27 | }
28 |
29 | // RegisterImpexModel registers model instance which supports InterfaceImpexModel interface to import/export system
30 | func RegisterImpexModel(name string, instance InterfaceImpexModel) error {
31 | if _, present := impexModels[name]; present {
32 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "8c5dba4f-6c54-4551-931a-a60d581252ab", name+" model already registered in impex")
33 | }
34 |
35 | impexModels[name] = instance
36 |
37 | return nil
38 | }
39 |
40 | // UnRegisterImpexModel un-registers InterfaceImpexModel capable model from import/export system
41 | func UnRegisterImpexModel(name string) error {
42 | if _, present := impexModels[name]; !present {
43 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "0b0863dd-f8ba-4a61-8438-603e649189e1", "can't find registered model "+name)
44 | }
45 |
46 | delete(impexModels, name)
47 |
48 | return nil
49 | }
50 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "os/signal"
7 | "syscall"
8 | "time"
9 |
10 | "github.com/ottemo/commerce/app"
11 |
12 | // using standard set of packages
13 | _ "github.com/ottemo/commerce/basebuild"
14 | "github.com/ottemo/commerce/env"
15 | )
16 |
17 | func init() {
18 | // time.Unix() should be in UTC (as it could be not by default)
19 | time.Local = time.UTC
20 | }
21 |
22 | // executable file start point
23 | func main() {
24 | defer func() {
25 | if err := app.End(); err != nil { // application close event
26 | fmt.Println(err.Error())
27 | }
28 | }()
29 |
30 | // we should intercept os signals to application as we should call app.End() before
31 | signalChain := make(chan os.Signal, 1)
32 | signal.Notify(signalChain, os.Interrupt, syscall.SIGTERM)
33 | go func() {
34 | for _ = range signalChain {
35 | err := app.End()
36 | if err != nil {
37 | _ = env.ErrorDispatch(err)
38 | fmt.Println(err.Error())
39 | }
40 |
41 | os.Exit(0)
42 | }
43 | }()
44 |
45 | // application start event
46 | if err := app.Start(); err != nil {
47 | _ = env.ErrorDispatch(err)
48 | fmt.Println(err.Error())
49 | os.Exit(0)
50 | }
51 |
52 | fmt.Println("Ottemo " + app.GetVerboseVersion())
53 |
54 | // starting HTTP server
55 | if err := app.Serve(); err != nil {
56 | fmt.Println(err.Error())
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/media/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package media represents media storage abstraction layer. It provides a set of interfaces and helpers to interact with
5 | media-storage services.
6 |
7 | If package decides to have media files related to model instances it should refer to that library for implementation
8 | of media storage list/read/write operations.
9 |
10 | Providing Ottemo with a new media storage supposing "InterfaceMediaStorage" implementation with following registration
11 | for media package.
12 | */
13 | package media
14 |
--------------------------------------------------------------------------------
/media/fsmedia/api.go:
--------------------------------------------------------------------------------
1 | package fsmedia
2 |
3 | import (
4 | "github.com/ottemo/commerce/api"
5 | "github.com/ottemo/commerce/media"
6 | "github.com/ottemo/commerce/utils"
7 | "strings"
8 | )
9 |
10 | // configures package related API endpoint routines
11 | func setupAPI() error {
12 |
13 | // api.GetRestService().GET("media/resizeAll", api.IsAdminHandler(APIResizeAll))
14 | api.GetRestService().GET("media/*path", APIGetMedia)
15 |
16 | return nil
17 | }
18 |
19 | func APIGetMedia(context api.InterfaceApplicationContext) (interface{}, error) {
20 | path := context.GetRequestArgument("path")
21 |
22 | mediaStorage, err := media.GetMediaStorage()
23 | if err != nil {
24 | return nil, err
25 | }
26 |
27 | result, err := mediaStorage.GetMediaByPath(path)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | // ref to https://www.sitepoint.com/mime-types-complete-list/
33 | if strings.HasSuffix(path, ".jpg") {
34 | context.SetResponseContentType("image/jpeg")
35 | } else if strings.HasSuffix(path, ".png") {
36 | context.SetResponseContentType("image/png")
37 | } else if strings.HasSuffix(path, ".avi") {
38 | context.SetResponseContentType("image/avi")
39 | } else if strings.HasSuffix(path, ".txt") {
40 | context.SetResponseContentType("text/plain")
41 | } else {
42 | context.SetResponseContentType("application/octet-stream")
43 | }
44 |
45 | return result, nil
46 | }
47 |
48 | // APIGetMediaInfo will resize all images if the params of the request contain 'resizeAll' with a value of true
49 | func APIResizeAll(context api.InterfaceApplicationContext) (interface{}, error) {
50 | // TODO: add example api call or add this to Apiary - jwv
51 |
52 | requestParams := context.GetRequestArguments()
53 | resizeAll := utils.GetFirstMapValue(requestParams, "resizeAll", "resizeImages", "resizeAllImages")
54 |
55 | if resizeAll != nil && utils.InterfaceToBool(resizeAll) {
56 | mediaStorage, err := media.GetMediaStorage()
57 | if err != nil {
58 | return nil, err
59 | }
60 |
61 | err = mediaStorage.ResizeAllMediaImages()
62 | if err != nil {
63 | return nil, err
64 | }
65 | }
66 |
67 | return "ok", nil
68 | }
69 |
--------------------------------------------------------------------------------
/media/fsmedia/decl.go:
--------------------------------------------------------------------------------
1 | package fsmedia
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // Package global constants
8 | const (
9 | ConstMediaDBCollection = "media" // database collection name to store media assignment information into
10 | ConstMediaDefaultFolder = "./media/" // filesystem folder path to store media files in there
11 |
12 | ConstResizeOnBackground = true
13 |
14 | ConstDefaultImageSize = "1000x1000" // "800x400"
15 | ConstDefaultImageSizes = "thumb: 280x350" // "small: 75x75, thumb: 260x300, big: 560x650"
16 |
17 | ConstConfigPathMediaImageSize = "general.app.image_size" // base image size
18 | ConstConfigPathMediaImageSizes = "general.app.image_sizes" // other image sizes required
19 |
20 | ConstConfigPathMediaBaseURL = "general.app.media_base_url"
21 |
22 | ConstErrorModule = "media/fsmedia"
23 | ConstErrorLevel = env.ConstErrorLevelService
24 | )
25 |
26 | // resizeImagesOnFly can be specified in ini config file by key "media.resize.images.onfly", false by default
27 | var (
28 | resizeImagesOnFly bool
29 | mediaBasePath = "media"
30 | )
31 |
32 | // FilesystemMediaStorage is a filesystem based implementer of InterfaceMediaStorage
33 | type FilesystemMediaStorage struct {
34 | storageFolder string
35 | setupWaitCnt int
36 |
37 | baseSize string
38 | biggestSize string
39 | imageSizes map[string]string
40 | }
41 |
--------------------------------------------------------------------------------
/media/fsmedia/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package fsmedia is a default implementation of InterfaceMediaStorage declared in "github.com/ottemo/commerce/media" package.
5 |
6 | It using filesystem ot store media files in, and database to store existing media to object bindings. Following media types
7 | special behaviour supposed:
8 | ConstMediaTypeImage ("image") - files stored in filesystem as set of specified image sizes
9 | ConstMediaTypeLink ("link") - external resource - not stored in filesystem
10 | ConstMediaTypeDocument ("document"), and others - file stored in filesystem but have not and image
11 |
12 | Files are stored within filesystem using following pattern: [ConstMediaDefaultFolder]/[mediaType]/[objectModelName]/[obejctID]/[mediaFileName].
13 |
14 | "image" type media re-sizes to [ConstConfigPathMediaImageSize] and set of [ConstConfigPathMediaImageSizes] sizes - these
15 | sizes are specified by config values (stored supposedly in DB). On program first startup them are initialized to
16 | [ConstDefaultImageSize] and [ConstDefaultImageSizes] (the same behaviour for config invalid values). Image resizing happens
17 | with usage of white background if [ConstResizeOnBackground] is set to true.
18 |
19 | Database record contain only "base image" record other sizes are named wit a following pattern:
20 | baseImage: [fileName].[fileExtension]
21 | sizedImage: [fileName]_[sizeName].[fileExtension]
22 |
23 | Image sizes is a config value like "small: 75x75, thumb: 260x300, big: 560x650", where key means [sizeName] and value is
24 | size [maxWidth]x[maxHeight] definition. If [sizeName] is not specified iw will equal to value (i.e. "75x75, thumb: 50x50"
25 | equals to "75x75: 75x75, thumb: 50x50"). Image re-sizes to [maxWidth]x[maxHeight] bounding box, "0" dimension have a
26 | special meaning - it means that in this direction image can be any size ("100x0" means that image height is not limited)
27 | Image re-sizing happens with keeping of image aspect ratio.
28 | */
29 | package fsmedia
30 |
--------------------------------------------------------------------------------
/media/interfaces.go:
--------------------------------------------------------------------------------
1 | package media
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // Package global constants
8 | const (
9 | ConstErrorModule = "media"
10 | ConstErrorLevel = env.ConstErrorLevelService
11 |
12 | ConstMediaTypeImage = "image"
13 | ConstMediaTypeLink = "link"
14 | ConstMediaTypeDocument = "document"
15 | )
16 |
17 | // InterfaceMediaStorage is an interface to access media storage service
18 | type InterfaceMediaStorage interface {
19 | GetName() string
20 |
21 | Load(model string, objID string, mediaType string, mediaName string) ([]byte, error)
22 | Save(model string, objID string, mediaType string, mediaName string, mediaData []byte) error
23 |
24 | Remove(model string, objID string, mediaType string, mediaName string) error
25 |
26 | ListMedia(model string, objID string, mediaType string) ([]string, error)
27 | ListMediaDetail(model string, objID string, mediaType string) ([]map[string]interface{}, error)
28 |
29 | GetMediaPath(model string, objID string, mediaType string) (string, error)
30 | GetMediaByPath(mediaPath string) ([]byte, error)
31 |
32 | GetAllSizes(model string, objID string, mediaType string) ([]map[string]string, error)
33 | GetSizes(model string, objID string, mediaType string, mediaName string) (map[string]string, error)
34 |
35 | ResizeAllMediaImages() error
36 | }
37 |
--------------------------------------------------------------------------------
/media/manager.go:
--------------------------------------------------------------------------------
1 | package media
2 |
3 | import (
4 | "github.com/ottemo/commerce/env"
5 | )
6 |
7 | // Package global variables
8 | var (
9 | currentMediaStorage InterfaceMediaStorage // currently registered media storage service in system
10 | callbacksOnMediaStorageStart = []func() error{} // set of callback function on media storage service start
11 | )
12 |
13 | // RegisterOnMediaStorageStart registers new callback on media storage service start
14 | func RegisterOnMediaStorageStart(callback func() error) {
15 | callbacksOnMediaStorageStart = append(callbacksOnMediaStorageStart, callback)
16 | }
17 |
18 | // OnMediaStorageStart fires media storage service start event (callback handling)
19 | func OnMediaStorageStart() error {
20 | for _, callback := range callbacksOnMediaStorageStart {
21 | if err := callback(); err != nil {
22 | return err
23 | }
24 | }
25 | return nil
26 | }
27 |
28 | // RegisterMediaStorage registers media storage service in the system
29 | // - will cause error if there are couple candidates for that role
30 | func RegisterMediaStorage(newEngine InterfaceMediaStorage) error {
31 | if currentMediaStorage == nil {
32 | currentMediaStorage = newEngine
33 | } else {
34 | return env.ErrorNew(ConstErrorModule, ConstErrorLevel, "eaf00795-12f6-4ea9-8ce2-7cc89088af14", "Sorry, '"+currentMediaStorage.GetName()+"' media storage already registered")
35 | }
36 | return nil
37 | }
38 |
39 | // GetMediaStorage returns currently used media storage service implementation
40 | func GetMediaStorage() (InterfaceMediaStorage, error) {
41 | if currentMediaStorage != nil {
42 | return currentMediaStorage, nil
43 | }
44 | return nil, env.ErrorNew(ConstErrorModule, ConstErrorLevel, "2bac26ac-67c6-4db4-9d07-1b813288a1af", "no registered media storage")
45 | }
46 |
--------------------------------------------------------------------------------
/ottemo.sample.ini:
--------------------------------------------------------------------------------
1 | ; Sample Configuration
2 |
3 | ; DB Connections
4 | ; MySQL Settings: [username]:[password]@[protocol[([address])]]/[dbname]
5 | db.mysql.db=ottemo
6 | db.mysql.uri=root@/
7 | db.mysql.maxConnections=50
8 | db.mysql.poolConnections=10
9 |
10 | ; Postgres Settings: postgres://[username]:[password]@[host]/[initial_schema]?[option=value,...]
11 | ; db.postgres.db=ottemo
12 | ; db.postgres.uri=postgres://localhost/postgres?sslmode=disable
13 | ; db.postgres.maxConnections=50
14 | ; db.postgres.poolConnections=10
15 |
16 | ; MongoDB Settings: [username]:[password]@[address:port]]/[dbname]
17 | ; mongodb.db=ottemo-dev
18 | ; mongodb.uri=mongodb://ottemo:ottemo@candidate.42.mongolayer.com:10243/ottemo-dev
19 |
20 | ; Other Settings
21 | media.fsmedia.folder=./media/
22 | media.resize.images.onfly=false
23 |
24 | secure_cookie=false
25 | xdomain.master=http://*.ottemo.io/
26 |
--------------------------------------------------------------------------------
/test/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Ottemo. All rights reserved.
2 |
3 | /*
4 | Package test represents set of test writing helpers and global application tests. It provides a set of functions you can
5 | use for starting Ottemo application in a test mode, prepare randomized data, fill DB with sample data, which is use-full
6 | during GO tests writing.
7 |
8 | Package also contains a set of benchmarks and tests which related to whole application rather when particular package.
9 | In order to run them use:
10 | go test [-tags ...] github.com/ottemo/commerce/tests
11 | go test -bench . [-tags ...] github.com/ottemo/commerce/tests
12 |
13 | (refer to http://golang.org/pkg/testing/ for details)
14 | */
15 | package test
16 |
--------------------------------------------------------------------------------
/utils/json.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | )
7 |
8 | // EncodeToJSONString encodes inputData to JSON string if it's possible
9 | func EncodeToJSONString(inputData interface{}) string {
10 | result, _ := json.Marshal(inputData)
11 | return string(result)
12 | }
13 |
14 | // DecodeJSONToArray decodes json string to []interface{} if it's possible
15 | func DecodeJSONToArray(jsonData interface{}) ([]interface{}, error) {
16 | var result []interface{}
17 |
18 | var err error
19 | switch value := jsonData.(type) {
20 | case string:
21 | err = json.Unmarshal([]byte(value), &result)
22 | case []byte:
23 | err = json.Unmarshal(value, &result)
24 | default:
25 | err = errors.New("Unsupported json type for conversion to array")
26 | }
27 |
28 | return result, err
29 | }
30 |
31 | // DecodeJSONToStringKeyMap decodes json string to map[string]interface{} if it's possible
32 | func DecodeJSONToStringKeyMap(jsonData interface{}) (map[string]interface{}, error) {
33 |
34 | result := make(map[string]interface{})
35 |
36 | var err error
37 |
38 | switch value := jsonData.(type) {
39 | case string:
40 | err = json.Unmarshal([]byte(value), &result)
41 | case []byte:
42 | err = json.Unmarshal(value, &result)
43 | default:
44 | err = errors.New("Unable to create map, unsupported json type")
45 | }
46 |
47 | return result, err
48 | }
49 |
50 | // DecodeJSONToInterface decodes json string to interface{} if it's possible
51 | func DecodeJSONToInterface(jsonData interface{}) (interface{}, error) {
52 |
53 | var result interface{}
54 |
55 | var err error
56 |
57 | switch value := jsonData.(type) {
58 | case string:
59 | err = json.Unmarshal([]byte(value), &result)
60 | case []byte:
61 | err = json.Unmarshal(value, &result)
62 | default:
63 | err = errors.New("Unable to parse json, unsupported json type")
64 | }
65 |
66 | return result, err
67 | }
68 |
--------------------------------------------------------------------------------
/utils/rounding_test.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestRoundPrice(t *testing.T) {
8 | if x := RoundPrice(32.87000000001); x != 32.87 {
9 | t.Error("incorect result:", x)
10 | }
11 | if x := RoundPrice(-32.87000000001); x != -32.87 {
12 | t.Error("incorect result:", x)
13 | }
14 |
15 | if x := RoundPrice(32.0); x != 32.0 {
16 | t.Error("incorect result:", x)
17 | }
18 | if x := RoundPrice(-32.0); x != -32.0 {
19 | t.Error("incorect result:", x)
20 | }
21 |
22 | if x := RoundPrice(32.865); x != 32.87 {
23 | t.Error("incorect result:", x)
24 | }
25 | if x := RoundPrice(-32.865); x != -32.87 {
26 | t.Error("incorect result:", x)
27 | }
28 |
29 | if x := RoundPrice(32.8699999999995); x != 32.87 {
30 | t.Error("incorect result:", x)
31 | }
32 | if x := RoundPrice(-32.8699999999995); x != -32.87 {
33 | t.Error("incorect result:", x)
34 | }
35 |
36 | if x := RoundPrice(0.0045); x != 0 {
37 | t.Error("incorect result:", x)
38 | }
39 | if x := RoundPrice(-0.0045); x != 0 {
40 | t.Error("incorect result:", x)
41 | }
42 |
43 | if x := RoundPrice(0.005); x != 0.01 {
44 | t.Error("incorect result:", x)
45 | }
46 | if x := RoundPrice(-0.005); x != -0.01 {
47 | t.Error("incorect result:", x)
48 | }
49 |
50 | if x := 1 - RoundPrice(0.000000000000009); x != 1 {
51 | t.Error("incorect result:", x)
52 | }
53 | if x := RoundPrice(0.000000000000009) - 1; x != -1 {
54 | t.Error("incorect result:", x)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/utils/templates.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "bytes"
5 | "text/template"
6 | )
7 |
8 | var (
9 | templateFuncs = make(map[string]interface{})
10 | )
11 |
12 | // RegisterTemplateFunction registers custom function within text template processing
13 | func RegisterTemplateFunction(name string, function interface{}) error {
14 | templateFuncs[name] = function
15 | return nil
16 | }
17 |
18 | // GetTemplateFunctions returns clone of templateFuncs (safe to manipulate)
19 | func GetTemplateFunctions() map[string]interface{} {
20 | result := make(map[string]interface{})
21 | for key, value := range templateFuncs {
22 | result[key] = value
23 | }
24 | return result
25 | }
26 |
27 | // TextTemplate evaluates text template, returns error if not possible
28 | func TextTemplate(templateContents string, context map[string]interface{}) (string, error) {
29 |
30 | textTemplate, err := template.New("TextTemplate").Funcs(templateFuncs).Parse(templateContents)
31 | if err != nil {
32 | return "", err
33 | }
34 |
35 | var result bytes.Buffer
36 | err = textTemplate.Execute(&result, context)
37 | if err != nil {
38 | return "", err
39 | }
40 |
41 | return result.String(), nil
42 | }
43 |
--------------------------------------------------------------------------------