├── .gitattributes ├── .gitignore ├── .travis.yml ├── generator-gin-api-0.0.2.tgz ├── .editorconfig ├── test └── app.js ├── generators └── app │ ├── templates │ ├── user.go │ ├── gitignore │ ├── objects.go │ ├── gin.go │ ├── _main.go │ ├── README.md │ ├── .editorconfig │ ├── routes.go │ ├── localConfig.yaml │ ├── localConfig_sample.yaml │ ├── gorm.go │ ├── Makefile │ ├── gin_suite_test.go │ ├── user_test.go │ └── config.go │ └── index.js ├── LICENSE ├── README.md ├── gulpfile.js └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - v6 4 | - v5 5 | - v4 6 | - '0.12' 7 | - '0.10' 8 | -------------------------------------------------------------------------------- /generator-gin-api-0.0.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sezzle/generator-gin-api/HEAD/generator-gin-api-0.0.2.tgz -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /test/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var assert = require('yeoman-assert'); 4 | var helpers = require('yeoman-test'); 5 | 6 | describe('generator-gin-api:app', function () { 7 | before(function () { 8 | return helpers.run(path.join(__dirname, '../generators/app')) 9 | .withPrompts({someAnswer: true}) 10 | .toPromise(); 11 | }); 12 | 13 | it('creates files', function () { 14 | assert.file([ 15 | '.gitignore' 16 | ]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /generators/app/templates/user.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "github.com/golang/glog" 5 | "github.com/jinzhu/gorm" 6 | ) 7 | 8 | type User struct { 9 | gorm.Model `json:"-"` 10 | FirstName string `json:"first_name,omitempty"` 11 | LastName string `json:"last_name,omitempty"` 12 | } 13 | 14 | // Save : Saves user object 15 | func (u *User) Create() error { 16 | err := DB.Create(u).Error 17 | if err != nil { 18 | glog.Info(err) 19 | return err 20 | } 21 | 22 | return err 23 | } 24 | -------------------------------------------------------------------------------- /generators/app/templates/gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS files 2 | .DS_Store 3 | *~ 4 | 5 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 6 | *.o 7 | *.a 8 | *.so 9 | 10 | # Editor additions 11 | .vscode 12 | 13 | config/localConfig.yaml 14 | config/localConfig.yml 15 | *tmp/ 16 | **tmp/* 17 | tmp/* 18 | 19 | # Architecture specific extensions/prefixes 20 | *.[568vq] 21 | [568vq].out 22 | 23 | *.cgo1.go 24 | *.cgo2.c 25 | _cgo_defun.c 26 | _cgo_gotypes.go 27 | _cgo_export.* 28 | 29 | _testmain.go 30 | 31 | *.exe 32 | *.test 33 | *.prof 34 | -------------------------------------------------------------------------------- /generators/app/templates/objects.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "github.com/golang/glog" 5 | ) 6 | 7 | //Migrate : Initializes all models in db 8 | func Migrate() error { 9 | glog.Info("Running object Migrations...") 10 | 11 | /* 12 | =========================================== 13 | Keep these alphabetical for easy search 14 | =========================================== 15 | */ 16 | 17 | glog.Info("Creating User Table") 18 | err := DB.AutoMigrate(&User{}).Error 19 | if err != nil { 20 | glog.Info(err) 21 | return err 22 | } 23 | 24 | return err 25 | } 26 | -------------------------------------------------------------------------------- /generators/app/templates/gin.go: -------------------------------------------------------------------------------- 1 | package gin 2 | 3 | import ( 4 | "<%= myAppPath %>/config" 5 | "strconv" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | var ( 11 | router *gin.Engine 12 | ) 13 | 14 | func init() { 15 | // initialize routes 16 | router = InitRoutes() 17 | } 18 | 19 | //Run starts a Gin server. 20 | func Run() { 21 | router.Run(":" + strconv.Itoa(config.ServerPort)) 22 | } 23 | 24 | //ReturnRouter returns a pointer to the engine 25 | func GetRouter() *gin.Engine { 26 | return router 27 | } 28 | 29 | //SetTestMode sets the gin mode as test. 30 | func SetTestMode() { 31 | gin.SetMode(gin.TestMode) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /generators/app/templates/_main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | _ "net/http/pprof" 6 | 7 | "<%= myAppPath %>/gin" 8 | "<%= myAppPath %>/gorm" 9 | 10 | "github.com/golang/glog" 11 | ) 12 | 13 | func main() { 14 | //Snag all flags that our application is run on. 15 | flag.Parse() 16 | flag.Lookup("alsologtostderr").Value.Set("true") 17 | 18 | //Initalize our db. 19 | glog.Info("Initalizing db...") 20 | db, err := gorm.InitDB() 21 | if err != nil { 22 | glog.Fatal("Could not initalize db", err.Error()) 23 | } 24 | 25 | 26 | //Defer this so that if our application exits, we close the db. 27 | //Double check this. 28 | 29 | defer db.Close() 30 | 31 | glog.Info("Initalizing Models...") 32 | 33 | err = gorm.Migrate() 34 | if err != nil { 35 | glog.Fatal("Could not run object migrations.") 36 | } 37 | 38 | gin.Run() 39 | 40 | } 41 | -------------------------------------------------------------------------------- /generators/app/templates/README.md: -------------------------------------------------------------------------------- 1 | # Go Generator Gin API 2 | 3 | ## Package Overviews 4 | 5 | * **config**: environment based configuration items 6 | * **gin**: core REST API package with all route handlers 7 | * **gorm**: persistence layer with connection setup for mysql 8 | * **gin_suite_test**: ginkgo packaged test suite for running integration style tests on API endpoints 9 | 10 | ## Configuration 11 | 12 | The `config/localConfig.go` is used as the local configuration file. This file is gitignored and should stay up to date with the `config/localConfig_sample.go` file. 13 | 14 | ## Adding Routes 15 | 16 | Routes are defined in `gin/routes.go` are initialized in the router on startup of the application. 17 | 18 | ## Adding Models 19 | 20 | All models are defined in the `gorm` package. Each model should have its own file and will be auto-migrated on initialization of the database. 21 | -------------------------------------------------------------------------------- /generators/app/templates/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*.{js,py}] 14 | charset = utf-8 15 | 16 | # 4 space indentation 17 | [*.py] 18 | indent_style = space 19 | indent_size = 4 20 | 21 | # Tab indentation (no size specified) 22 | [Makefile] 23 | indent_style = tab 24 | 25 | # Indentation override for all JS under lib directory 26 | [lib/**.js] 27 | indent_style = space 28 | indent_size = 2 29 | 30 | # Matches the exact files either package.json or .travis.yml 31 | [{package.json,.travis.yml}] 32 | indent_style = space 33 | indent_size = 2 34 | 35 | [*.yaml] 36 | ident_style = space 37 | ident_size = 2 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Killian Brackey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /generators/app/templates/routes.go: -------------------------------------------------------------------------------- 1 | package gin 2 | 3 | import ( 4 | "time" 5 | "<%= myAppPath %>/config" 6 | 7 | "github.com/gin-gonic/gin" 8 | cors "github.com/itsjamie/gin-cors" 9 | ) 10 | 11 | // InitRoutes : Creates all of the routes for our application and returns a router 12 | func InitRoutes() *gin.Engine { 13 | 14 | router := gin.New() 15 | 16 | router.Use(gin.Logger()) 17 | router.Use(gin.Recovery()) 18 | 19 | // Apply the middleware to the router (works with groups too) 20 | router.Use(cors.Middleware(cors.Config{ 21 | Origins: "*", //cfg.Origins, 22 | Methods: "GET, PUT, POST, DELETE", 23 | RequestHeaders: "Origin, Authorization, Content-Type", 24 | // ExposedHeaders: "", 25 | MaxAge: 50 * time.Second, 26 | Credentials: true, 27 | ValidateHeaders: true, //Should be true for production. - is more secure because we validate headers as opposed to ember. 28 | })) 29 | 30 | if config.Debug { 31 | debugGroup := router.Group("/debug") 32 | setDebugRoutes(debugGroup) 33 | } 34 | 35 | v1 := router.Group("/v1") 36 | { 37 | setUserRoutes(v1) 38 | } 39 | 40 | return router 41 | } 42 | 43 | 44 | //setUserRoutes : 45 | func setUserRoutes(g *gin.RouterGroup) { 46 | g.POST("/user") 47 | } 48 | 49 | func setDebugRoutes(g *gin.RouterGroup) { 50 | g.GET("/test") 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /generators/app/templates/localConfig.yaml: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Local Settings configuration, please keep gitignored. # 3 | # Must be named localConfig.yaml (or .yml) # 4 | # REQUIRED : Must change values if using local settings file for configuraion # 5 | ############################################################################### 6 | 7 | dbUsername: "sezzle" 8 | dbPassword: "Testing123" 9 | dbName: "sezzle" 10 | 11 | dbHostName: "127.0.0.1" 12 | dbPort: 3306 13 | dbDriver: "mysql" # Mysql recommended 14 | 15 | ############################# 16 | # Other Services # 17 | ############################# 18 | 19 | otherServiceURL: http://localhost:4200 20 | 21 | ############################# 22 | # Optional, but recommended # 23 | ############################# 24 | 25 | debug: true # Highly recommended 26 | dbRootPassword: "DBROOTPASSWORD" # Required root test db, optional 27 | testDbName: "test" # REQUIRES root password for use 28 | 29 | ################################## 30 | # Optional: Not likely to change # 31 | ################################## 32 | 33 | serverHostName: "http://localhost" # Hostname to run this api on 34 | serverPort: "10000" # Port to run this api on 35 | -------------------------------------------------------------------------------- /generators/app/templates/localConfig_sample.yaml: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Local Settings configuration, please keep gitignored. # 3 | # Must be named localConfig.yaml (or .yml) # 4 | # REQUIRED : Must change values if using local settings file for configuraion # 5 | ############################################################################### 6 | 7 | dbUsername: "sezzle" 8 | dbPassword: "Testing123" 9 | dbName: "sezzle" 10 | 11 | dbHostName: "127.0.0.1" 12 | dbPort: 3306 13 | dbDriver: "mysql" # Mysql recommended 14 | 15 | ############################# 16 | # Other Services # 17 | ############################# 18 | 19 | otherServiceURL: http://localhost:4200 20 | 21 | ############################# 22 | # Optional, but recommended # 23 | ############################# 24 | 25 | debug: true # Highly recommended 26 | dbRootPassword: "DBROOTPASSWORD" # Required root test db, optional 27 | testDbName: "test" # REQUIRES root password for use 28 | 29 | ################################## 30 | # Optional: Not likely to change # 31 | ################################## 32 | 33 | serverHostName: "http://localhost" # Hostname to run this api on 34 | serverPort: "10000" # Port to run this api on 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # generator-gin-api [![NPM version][npm-image]][npm-url][![Dependency Status][daviddm-image]][daviddm-url] 2 | > Project generator for gin, gorm golang api 3 | 4 | ## Installation 5 | 6 | First, install [Yeoman](http://yeoman.io) and generator-gin-api using [npm](https://www.npmjs.com/) (we assume you have pre-installed [node.js](https://nodejs.org/)). 7 | 8 | ```bash 9 | npm install -g yo 10 | npm install -g generator-gin-api 11 | ``` 12 | 13 | Then generate your new project: 14 | 15 | ```bash 16 | cd $GOPATH/src/github.com && mkdir myNewProject && cd myNewProject 17 | yo gin-api 18 | ``` 19 | 20 | ### Note: You will want to generate this project within your $GOPATH 21 | 22 | ## Getting To Know Yeoman 23 | 24 | * Yeoman has a heart of gold. 25 | * Yeoman is a person with feelings and opinions, but is very easy to work with. 26 | * Yeoman can be too opinionated at times but is easily convinced not to be. 27 | * Feel free to [learn more about Yeoman](http://yeoman.io/). 28 | 29 | ## License 30 | 31 | MIT © [Killian Brackey]() 32 | 33 | 34 | [npm-image]: https://badge.fury.io/js/generator-gin-api.svg 35 | [npm-url]: https://npmjs.org/package/generator-gin-api 36 | [travis-image]: https://travis-ci.org/sezzle/generator-gin-api.svg?branch=master 37 | [travis-url]: https://travis-ci.org/sezzle/generator-gin-api 38 | [daviddm-image]: https://david-dm.org/sezzle/generator-gin-api.svg?theme=shields.io 39 | [daviddm-url]: https://david-dm.org/sezzle/generator-gin-api 40 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var gulp = require('gulp'); 4 | var eslint = require('gulp-eslint'); 5 | var excludeGitignore = require('gulp-exclude-gitignore'); 6 | var mocha = require('gulp-mocha'); 7 | var istanbul = require('gulp-istanbul'); 8 | var nsp = require('gulp-nsp'); 9 | var plumber = require('gulp-plumber'); 10 | 11 | gulp.task('static', function () { 12 | return gulp.src('**/*.js') 13 | .pipe(excludeGitignore()) 14 | .pipe(eslint()) 15 | .pipe(eslint.format()) 16 | .pipe(eslint.failAfterError()); 17 | }); 18 | 19 | gulp.task('nsp', function (cb) { 20 | nsp({package: path.resolve('package.json')}, cb); 21 | }); 22 | 23 | gulp.task('pre-test', function () { 24 | return gulp.src('generators/**/*.js') 25 | .pipe(excludeGitignore()) 26 | .pipe(istanbul({ 27 | includeUntested: true 28 | })) 29 | .pipe(istanbul.hookRequire()); 30 | }); 31 | 32 | gulp.task('test', ['pre-test'], function (cb) { 33 | var mochaErr; 34 | 35 | gulp.src('test/**/*.js') 36 | .pipe(plumber()) 37 | .pipe(mocha({reporter: 'spec'})) 38 | .on('error', function (err) { 39 | mochaErr = err; 40 | }) 41 | .pipe(istanbul.writeReports()) 42 | .on('end', function () { 43 | cb(mochaErr); 44 | }); 45 | }); 46 | 47 | gulp.task('watch', function () { 48 | gulp.watch(['generators/**/*.js', 'test/**'], ['test']); 49 | }); 50 | 51 | gulp.task('prepublish'); 52 | gulp.task('default', ['static', 'test']); 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-gin-api", 3 | "version": "1.0.0", 4 | "description": "Project generator for gin, gorm golang api", 5 | "homepage": "https://sezzle.com", 6 | "author": { 7 | "name": "Killian Brackey", 8 | "email": "killian.brackey@sezzle.com", 9 | "url": "https://github.com/sezzle/generator-gin-api" 10 | }, 11 | "files": [ 12 | "generators" 13 | ], 14 | "main": "generators/index.js", 15 | "keywords": [ 16 | "go", 17 | "golang", 18 | "gin", 19 | "gorm", 20 | "yeoman-generator" 21 | ], 22 | "dependencies": { 23 | "chalk": "^2.4.1", 24 | "generator-git-init": "^1.1.3", 25 | "yeoman-generator": "^0.23.0", 26 | "yosay": "^1.0.0" 27 | }, 28 | "devDependencies": { 29 | "eslint": "^3.1.1", 30 | "eslint-config-xo-space": "^0.14.0", 31 | "generator-git-init": "^1.1.3", 32 | "gulp": "^3.9.0", 33 | "gulp-eslint": "^2.0.0", 34 | "gulp-exclude-gitignore": "^1.0.0", 35 | "gulp-istanbul": "^1.0.0", 36 | "gulp-line-ending-corrector": "^1.0.1", 37 | "gulp-mocha": "^2.0.0", 38 | "gulp-nsp": "^2.1.0", 39 | "gulp-plumber": "^1.0.0", 40 | "mkdirp": "^0.5.1", 41 | "yeoman-assert": "^2.0.0", 42 | "yeoman-test": "^1.0.0", 43 | "yo": "^1.8.5" 44 | }, 45 | "eslintConfig": { 46 | "extends": "xo-space", 47 | "env": { 48 | "mocha": true 49 | } 50 | }, 51 | "repository": "sezzle/generator-gin-api", 52 | "scripts": { 53 | "prepublish": "gulp prepublish", 54 | "test": "gulp" 55 | }, 56 | "license": "MIT" 57 | } 58 | -------------------------------------------------------------------------------- /generators/app/templates/gorm.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/golang/glog" 8 | "github.com/jinzhu/gorm" 9 | "<%= myAppPath %>/config" 10 | 11 | // recommended by gorm to have this blank import 12 | _ "github.com/jinzhu/gorm/dialects/mysql" 13 | ) 14 | 15 | 16 | func init() { 17 | } 18 | 19 | //Constants defined across 20 | const ( 21 | numOfReq int = 25 //The number of requests to ping the db while waiting for startup 22 | timePerReq time.Duration = 5 //The amount of time to wait per request. num*time = total time to wait for db initalize. 23 | ) 24 | 25 | var ( 26 | // DB is connectino handle for the db 27 | DB *gorm.DB 28 | //TestDB is connection handle for a test database 29 | TestDB *gorm.DB 30 | ) 31 | 32 | //InitDB : This Initalizes the first db, and exports it to be passed around. 33 | func InitDB() (*gorm.DB, error) { 34 | 35 | //Create the Url used to Open the db 36 | dbURL := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true&loc=UTC", config.DbUsername, config.DbPassword, config.DbHostname, config.DbPort, config.DbName) 37 | 38 | //Attempt to open a new connect to the db 39 | glog.Info("Opening a connection to the db...") 40 | db, err := gorm.Open(config.DbDriver, dbURL) 41 | if err != nil { 42 | glog.Info("Couldn't open a connection to the db!", err) 43 | return nil, err 44 | } 45 | 46 | // GOING TO KEEP IN DEBUG MODE REGARDLESS OF DEBUG SETTING 47 | //Debug settings for db 48 | // if Debug == "true" { 49 | db.LogMode(true) 50 | // } 51 | 52 | //Set our Variable to use this connection 53 | DB = db 54 | 55 | // Limit our idle connections to 10, with a maximum lifetime of 30 seconds 56 | db.DB().SetMaxIdleConns(100) 57 | db.DB().SetConnMaxLifetime(30 * time.Second) 58 | 59 | // Limit total connections to 10 60 | db.DB().SetMaxOpenConns(100) 61 | 62 | return DB, err 63 | } 64 | -------------------------------------------------------------------------------- /generators/app/templates/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build fmt lint dev test vet godep install bench run fresh 2 | 3 | SHELL := /bin/sh 4 | PKG_NAME=$(shell basename `pwd`) 5 | GIT_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) 6 | GINKGO := $(shell command -v ginkgo 2> /dev/null) 7 | 8 | vendor-dependencies: 9 | govendor add +e 10 | 11 | build: vet \ 12 | test 13 | go build 14 | 15 | doc: 16 | godoc -http=:6060 17 | 18 | fmt: 19 | go fmt 20 | 21 | lint: 22 | golint ./... | grep -v vendor 23 | 24 | dev: 25 | DEBUG=* go get && govendor add +e && go run main.go 26 | 27 | fresh: 28 | go get github.com/pilu/fresh && export ENVIRONMENT=local && fresh 29 | 30 | # Runs Tests with single stream logs for ginkgo tests 31 | test: 32 | # Install ginkgo if not installed 33 | ifeq ($(GINKGO),) 34 | go get -u github.com/onsi/ginkgo/ginkgo 35 | endif 36 | # Run all Gin tests with ginkgo 37 | export ENVIRONMENT=testing && ginkgo -r -v -noisyPendings=false --nodes=1 -notify --progress --trace ./gin | grep -v vendor 38 | 39 | # Runs all tests 40 | test-all: 41 | export ENVIRONMENT=testing && go test -v ./... -cover | grep -v vendor 42 | 43 | test-only: 44 | export ENVIRONMENT=testing && ginkgo -r -v -noisyPendings=false --nodes=1 -notify --progress --trace ./gin --focus="ONLY" 45 | 46 | bench: 47 | go test ./... -bench=. | grep -v vendor 48 | 49 | vet: 50 | go vet 51 | 52 | commit: 53 | read -r -p "Commit message: " message; \ 54 | git add .; \ 55 | git commit -m "$$message" \ 56 | 57 | # note pushes to origin 58 | push: build 59 | git push origin $(GIT_BRANCH) 60 | 61 | cover: 62 | read -r -p "package to get coverage from: " package; \ 63 | export ENVIRONMENT=testing && go test -v ./"$$package" --cover --coverprofile=coverage.out | grep -v vendor \ 64 | 65 | go tool cover -html=coverage.out 66 | 67 | # watches and runs ginkgo tests and notifies on failures 68 | watch: 69 | 70 | export ENVIRONMENT=testing && ginkgo watch -r -v -noisyPendings=false --nodes=1 -notify --progress --trace ./gin | grep -v vendor 71 | 72 | prune: 73 | 74 | git branch --merged | egrep -v "(^\*|staging)" | xargs git branch -d && git remote prune origin 75 | -------------------------------------------------------------------------------- /generators/app/templates/gin_suite_test.go: -------------------------------------------------------------------------------- 1 | package gin_test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "flag" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "net/http/httptest" 11 | 12 | "github.com/golang/glog" 13 | "<%= myAppPath %>/config" 14 | testapi "<%= myAppPath %>/gin" 15 | testgorm "<%= myAppPath %>/gorm" 16 | "testing" 17 | 18 | "github.com/gin-gonic/gin" 19 | . "github.com/onsi/ginkgo" 20 | . "github.com/onsi/gomega" 21 | ) 22 | 23 | func init() { 24 | flag.Set("alsologtostderr", "true") 25 | flag.Set("v", "10") 26 | } 27 | 28 | var ( 29 | s *gin.Engine 30 | response *httptest.ResponseRecorder 31 | request *http.Request 32 | endpointURL string = "" 33 | endpointMethod string 34 | endpointHeaders http.Header 35 | form interface{} 36 | ) 37 | 38 | type errorReply map[string][]string 39 | type keyValueResp map[string]string 40 | 41 | func TestGin(t *testing.T) { 42 | RegisterFailHandler(Fail) 43 | 44 | BeforeSuite(func() { 45 | // Initialize an in memory database (WIP) 46 | config.SetSettingsFromViper() 47 | var testDBName string = config.DbName 48 | // db.LogMode(true) 49 | db, err := testgorm.InitDB() 50 | 51 | //Drop old test db 52 | dropDBStmt := fmt.Sprintf(`DROP DATABASE IF EXISTS %s`, testDBName) 53 | db.Exec(dropDBStmt) 54 | 55 | //TODO: lock tables 56 | 57 | //Create Database 58 | createStmt := fmt.Sprintf(`CREATE DATABASE %s;`, testDBName) 59 | result := db.Exec(createStmt) 60 | if result.Error != nil { 61 | glog.Error(result.Error) 62 | } 63 | 64 | //Select new test database 65 | useStmnt := fmt.Sprintf(`USE %s;`, testDBName) 66 | result = db.Exec(useStmnt) 67 | if result.Error != nil { 68 | glog.Error(result.Error) 69 | } 70 | 71 | err = testgorm.Migrate() 72 | if err != nil { 73 | glog.Fatal("Could not run object migrations.") 74 | } 75 | 76 | gin.SetMode(gin.ReleaseMode) 77 | s = testapi.InitRoutes() 78 | 79 | }) 80 | 81 | AfterSuite(func() { 82 | //Drop old test db 83 | //dropDBStmt := fmt.Sprintf(`DROP DATABASE IF EXISTS %s`, testDBName) 84 | }) 85 | 86 | RunSpecs(t, "Gin Suite") 87 | } 88 | 89 | //DecodeTestJson Decodes response body to interface provided 90 | func DecodeTestJson(response *httptest.ResponseRecorder, decodeStruct interface{}) error { 91 | htmlData, err := ioutil.ReadAll(response.Body) 92 | if err != nil { 93 | glog.Error(err) 94 | return err 95 | } 96 | 97 | decoder := json.NewDecoder(bytes.NewBuffer(htmlData)) 98 | if err = decoder.Decode(decodeStruct); err != nil { 99 | return err 100 | } 101 | 102 | return nil 103 | } 104 | -------------------------------------------------------------------------------- /generators/app/templates/user_test.go: -------------------------------------------------------------------------------- 1 | package gin_test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "flag" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | 11 | "github.com/golang/glog" 12 | 13 | "github.com/gin-gonic/gin" 14 | . "github.com/onsi/ginkgo" 15 | . "github.com/onsi/gomega" 16 | ) 17 | 18 | func init() { 19 | if testing.Verbose() { 20 | flag.Set("alsologtostderr", "true") 21 | flag.Set("v", "5") 22 | } 23 | } 24 | 25 | var ( 26 | jsonErr error 27 | ) 28 | 29 | var _ = Describe("SignupHandler", func() { 30 | RegisterFailHandler(Fail) 31 | 32 | BeforeEach(func() { 33 | endpointHeaders = make(http.Header) 34 | endpointHeaders.Add("X-Real-IP", "74.37.200.161") //Setting a fake IP address for login security tests. 35 | form = gin.H{} 36 | }) 37 | 38 | JustBeforeEach(func() { 39 | glog.Error("Fire request") 40 | response = httptest.NewRecorder() 41 | 42 | if (endpointMethod == "POST") || (endpointMethod == "PUT") || endpointMethod == "PATCH" { 43 | jsonString, _ := json.Marshal(form) 44 | var err error 45 | request, err = http.NewRequest(endpointMethod, endpointURL, bytes.NewReader(jsonString)) 46 | if err != nil { 47 | glog.Error(err) 48 | } 49 | request.Header = endpointHeaders 50 | request.Header.Add("Content-Type", "application/json") 51 | 52 | } else { 53 | request, _ = http.NewRequest(endpointMethod, endpointURL, nil) 54 | request.Header = endpointHeaders 55 | } 56 | 57 | s.ServeHTTP(response, request) 58 | }) 59 | 60 | //g.GET("/debug/test",) 61 | Describe("GET /debug/test", func() { 62 | var responseKeyValue keyValueResp 63 | BeforeEach(func() { 64 | endpointMethod = "GET" 65 | endpointURL = "http://localhost:8000/debug/test" 66 | }) 67 | 68 | Context("On testing the debut route", func() { 69 | BeforeEach(func() { 70 | form = gin.H{} 71 | }) 72 | 73 | JustBeforeEach(func() { 74 | jsonErr = DecodeTestJson(response, &responseKeyValue) 75 | }) 76 | 77 | It("should return an error", func() { 78 | Ω(response.Code).Should(Equal(http.StatusOK)) 79 | // Ω(jsonErr).ShouldNot(HaveOccurred()) 80 | // Ω(responseKeyValue).ShouldNot(BeEmpty()) 81 | }) 82 | }) 83 | 84 | Context("On next context", func() { 85 | BeforeEach(func() { 86 | form = gin.H{} 87 | endpointMethod = "POST" 88 | endpointURL = "http://localhost:8000/v1/user" 89 | }) 90 | 91 | JustBeforeEach(func() { 92 | jsonErr = DecodeTestJson(response, &responseKeyValue) 93 | }) 94 | 95 | It("Return status ok", func() { 96 | Ω(response.Code).Should(Equal(http.StatusOK)) 97 | // Ω(jsonErr).ShouldNot(HaveOccurred()) 98 | // Ω(responseKeyValue).ShouldNot(BeEmpty()) 99 | // Ω(responseKeyValue).Should(HaveKeyWithValue("name", "Test Name")) 100 | }) 101 | }) 102 | }) 103 | 104 | }) 105 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var yeoman = require('yeoman-generator'); 3 | var mkdirp = require('mkdirp'); 4 | 5 | var chalk = require('chalk'); 6 | var yosay = require('yosay'); 7 | 8 | module.exports = yeoman.Base.extend({ 9 | prompting: function () { 10 | // Have Yeoman greet the user. 11 | this.log(chalk.hex('#6fd6e3').bold(` 12 | ╭──────────────────────────╮ 13 | │ Welcome to the terrific │ 14 | │ generator-gin-api │ 15 | │ generator! │ 16 | ╰──────────────────────────╯ 17 | ### 18 | ## 19 | # 20 | dhyysoo++++ooossyhd 21 | Ndyo/:#######################:+sd 22 | NN/:###ds/#######################/+////+/###+y####### 23 | Ny/:###/hy:#:+/::###::/+:#########+/:" .:# :+/##:oo###### 24 | h:##//:+o:#:+: +hdy. #+:#####++" s####" :+###:sN +###: 25 | ###d /##:o :##### o:###// h#s#N. :/####+Ny#### 26 | d###sN :###s" "sd+d: "o###s ://" s#####++###/ 27 | y:##s/####s "s###s" "s######s:/ 28 | Nhso#####// +:###:o" "o:######: 29 | N#######+/ "+/:+sss+o/" ./+######### 30 | y########:+/:. "#://#+N N:+//:::::/+/###########/ 31 | +############:/+++/:##ooodNNN dsoo+##################### 32 | /####################s/:::::::::::/s####################h 33 | :####################+o//+++s/++//oo####################s 34 | :######################o/ ## ++/:#####################o 35 | /######################:+ :: :/#######################+ 36 | +#######################+//++//+########################+`)); 37 | 38 | this.log(chalk.hex('#c0a98e').bold('\n' + 39 | ' +------------------------------------------------+\n' + 40 | ' | G o G I N | p r o j e c t | g e n e r a t o r |\n' + 41 | ' +------------------------------------------------+\n' + 42 | '\n')); 43 | 44 | return this.prompt([{ 45 | type: 'input', 46 | name: 'myAppPath', 47 | message: 'What is the root path for your project (ex: github.com/sezzle/generator-gin-api)', 48 | default: process.cwd().replace(process.env.GOPATH + "/src/", '') 49 | }]).then(function (answers) { 50 | this.log('app name', answers.myAppPath); 51 | this.myAppPath = answers.myAppPath; 52 | }.bind(this)); 53 | }, 54 | 55 | buildTreeFolderAndCopyFiles: function () { 56 | console.log('Generating tree folders'); 57 | var configDir = 'config/'; 58 | var ginDir = 'gin/'; 59 | var gormDir = 'gorm/'; 60 | var deployDir = 'deploy/'; 61 | 62 | mkdirp(configDir); 63 | mkdirp(ginDir); 64 | mkdirp(gormDir); 65 | mkdirp(deployDir); 66 | 67 | this.copy('gitignore', '.gitignore'); 68 | this.copy('.editorconfig', '.editorconfig'); 69 | this.copy('README.md', 'README.md'); 70 | this.copy('config.go', configDir + 'config.go'); 71 | this.copy('localConfig.yaml', configDir + 'localConfig.yaml'); 72 | this.copy('localConfig_sample.yaml', configDir + 'localConfig_sample.yaml'); 73 | this.copy('gorm.go', gormDir + 'gorm.go'); 74 | this.copy('user.go', gormDir + 'user.go'); 75 | this.copy('gin.go', ginDir + 'gin.go'); 76 | this.copy('routes.go', ginDir + 'routes.go'); 77 | this.copy('objects.go', gormDir + 'objects.go'); 78 | this.copy('gin_suite_test.go', ginDir + 'gin_suite_test.go'); 79 | this.copy('user_test.go', ginDir + 'user_test.go'); 80 | this.copy('Makefile', 'Makefile'); 81 | 82 | var tmplContext = { 83 | myAppPath : this.myAppPath, 84 | }; 85 | 86 | this.template('_main.go', 'main.go', tmplContext); 87 | 88 | }, 89 | 90 | // initialize git repo 91 | initializing: function () { 92 | console.log('Initializing git repository'); 93 | this.spawnCommandSync('git', ['init', '--quiet']); 94 | }, 95 | 96 | // start govendor directory 97 | install: function () { 98 | console.log('Running go get on dependencies...'); 99 | this.spawnCommandSync('go', ['get']); 100 | 101 | console.log('Updating installation of govendor'); 102 | this.spawnCommandSync('go', ['get', '-u', 'github.com/kardianos/govendor']); 103 | 104 | console.log('Initializing govendor'); 105 | this.spawnCommandSync('govendor', ['init']); 106 | 107 | console.log('Vendoring external dependencies'); 108 | this.spawnCommandSync('make', ['vendor-dependencies']); 109 | 110 | console.log('Vendoring Gomega (see https://github.com/onsi/gomega/issues/156)'); 111 | this.spawnCommandSync('govendor', ['fetch', 'github.com/onsi/gomega']); 112 | } 113 | 114 | }); 115 | -------------------------------------------------------------------------------- /generators/app/templates/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | 8 | "github.com/golang/glog" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | func init() { 13 | SetSettingsFromViper() 14 | } 15 | 16 | //Settings : Contains all of the envs that need to be grabbed for this application 17 | 18 | var ( 19 | // ServerPort : port to run gin server on 20 | ServerPort int 21 | //ServerHostName : Hostname to run this server on 22 | ServerHostName string 23 | // Debug mode for otp messages 24 | Debug bool 25 | //DbDriver : The Driver for the DB - Mysql for now, although we can test others.package settings 26 | DbDriver string 27 | // DbUsername : Username to loging to db 28 | DbUsername string 29 | // DbPassword : Password for db 30 | DbPassword string 31 | //DbRootPassword : Root Password for db, used to create test db *Local Env Only 32 | DbRootPassword string 33 | //DbHostname : Location of the db in the cluster. Usually surrounded by tcp() 34 | DbHostname string 35 | //DbPort : Port that our db is open at - traditionally 3306 36 | DbPort string 37 | //DbName : The name of the specific db. 38 | DbName string 39 | // TestDBName : Default: test | Set in local environment to avoid name clash 40 | TestDBName string 41 | 42 | // Environment : dev environment, production, docker, etc 43 | Environment AppEnvironment 44 | 45 | // AppEnvironments : array of all app environments 46 | AppEnvironments = []AppEnvironment{ 47 | AppEnvironmentTesting, 48 | AppEnvironmentLocal, 49 | AppEnvironmentStaging, 50 | AppEnvironmentProduction, 51 | } 52 | ) 53 | 54 | // AppEnvironment : string wrapper for environment name 55 | type AppEnvironment string 56 | 57 | const ( 58 | // AppEnvironmentTesting : testing env 59 | AppEnvironmentTesting = AppEnvironment("testing") 60 | // AppEnvironmentLocal : 61 | AppEnvironmentLocal = AppEnvironment("local") 62 | // AppEnvironmentStaging : 63 | AppEnvironmentStaging = AppEnvironment("staging") 64 | // AppEnvironmentProduction : 65 | AppEnvironmentProduction = AppEnvironment("production") 66 | ) 67 | 68 | //SetSettingsFromViper : Sets global settings using viper 69 | func SetSettingsFromViper() { 70 | Environment = getEnvironment() 71 | glog.Info("We're in our the following environment: ", Environment) 72 | // SetENV if not in a production environment 73 | // Check for local 74 | if Environment != AppEnvironmentProduction && Environment != AppEnvironmentStaging { 75 | setEnvironmentVariablesFromConfig(Environment) 76 | } 77 | 78 | if Environment == AppEnvironmentTesting { 79 | DbName = os.Getenv("TEST_DB_NAME") 80 | } else { 81 | DbName = os.Getenv("DB_NAME") 82 | } 83 | 84 | DbDriver = os.Getenv("DB_DRIVER") 85 | DbHostname = os.Getenv("DB_HOSTNAME") 86 | DbUsername = os.Getenv("DB_USERNAME") 87 | DbPort = os.Getenv("DB_PORT") 88 | DbName = os.Getenv("DB_NAME") 89 | DbPassword = os.Getenv("DB_PASSWORD") 90 | glog.Info("Db settings: ", DbDriver, " ", DbHostname, " ", DbName) 91 | 92 | Debug, _ = strconv.ParseBool(os.Getenv("DEBUG")) 93 | ServerHostName = os.Getenv("SERVER_HOSTNAME") 94 | ServerPort, _ = strconv.Atoi(os.Getenv("SERVER_PORT")) 95 | } 96 | 97 | func setEnvironmentVariablesFromConfig(env AppEnvironment) { 98 | // get and set basePath of project 99 | baseProjectPath := fmt.Sprintf("%s/src/<%= myAppPath %>/", os.Getenv("GOPATH")) 100 | viper.AddConfigPath(baseProjectPath + "/config/") 101 | viper.SetConfigType("yaml") 102 | viper.SetConfigName("localConfig") 103 | 104 | viper.SetConfigType("yaml") 105 | 106 | err := viper.ReadInConfig() 107 | if err != nil { 108 | glog.Info("Failed reading local settings: ", err) 109 | } 110 | debug := viper.GetBool("debug") 111 | 112 | serverHostName := viper.GetString("serverHostName") 113 | serverPort := viper.GetString("serverPort") 114 | dbDriver := viper.GetString("dbDriver") 115 | dbHostname := viper.GetString("dbHostName") 116 | dbPassword := viper.GetString("dbPassword") 117 | dbRootPassword := viper.GetString("dbRootPassword") 118 | dbPort := viper.GetString("dbPort") 119 | dbUser := viper.GetString("dbUsername") 120 | dbName := viper.GetString("dbName") 121 | dbTestDBName := viper.GetString("testDbName") 122 | 123 | // Set the OS Environment variables 124 | os.Setenv("DB_DRIVER", dbDriver) 125 | os.Setenv("DB_HOSTNAME", dbHostname) 126 | os.Setenv("DB_USERNAME", dbUser) 127 | os.Setenv("DB_PORT", dbPort) 128 | os.Setenv("DB_NAME", dbName) 129 | os.Setenv("DB_PASSWORD", dbPassword) 130 | os.Setenv("DB_ROOTPASSWORD", dbRootPassword) 131 | os.Setenv("TEST_DB_NAME", dbTestDBName) 132 | os.Setenv("DEBUG", strconv.FormatBool(debug)) 133 | os.Setenv("SERVER_HOSTNAME", serverHostName) 134 | os.Setenv("SERVER_PORT", serverPort) 135 | glog.Info("setEnvironmentVariablesFromConfig: Config finished reading in settings from file.") 136 | 137 | } 138 | 139 | func getEnvironment() AppEnvironment { 140 | hostEnvironment := os.Getenv("ENVIRONMENT") 141 | for _, env := range AppEnvironments { 142 | if env == AppEnvironment(hostEnvironment) { 143 | Environment = env 144 | return env 145 | } 146 | } 147 | 148 | // set to local config if environment not found 149 | return AppEnvironmentLocal 150 | } 151 | --------------------------------------------------------------------------------