diff --git a/lab-raziyeh/.babelrc b/lab-raziyeh/.babelrc new file mode 100644 index 0000000..c13c5f6 --- /dev/null +++ b/lab-raziyeh/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015"] +} diff --git a/lab-raziyeh/.eslintignore b/lab-raziyeh/.eslintignore new file mode 100644 index 0000000..1cff4b4 --- /dev/null +++ b/lab-raziyeh/.eslintignore @@ -0,0 +1,5 @@ +**/node_modules/* +**/vendor/* +**/*.min.js +**/coverage/* +**/build/** diff --git a/lab-raziyeh/.eslintrc b/lab-raziyeh/.eslintrc new file mode 100644 index 0000000..7785703 --- /dev/null +++ b/lab-raziyeh/.eslintrc @@ -0,0 +1,23 @@ +{ + "rules": { + "comma-dangle": ["error", "always-multiline"], + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": [ "error", "single" ], + "linebreak-style": [ "error", "unix" ] + }, + "env": { + "es6": true, + "node": true, + "mocha": true, + "jasmine": true + }, + "globals": { + }, + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true, + "impliedStrict": true + }, + "extends": "eslint:recommended" +} diff --git a/lab-raziyeh/.gitignore b/lab-raziyeh/.gitignore new file mode 100644 index 0000000..2e701b2 --- /dev/null +++ b/lab-raziyeh/.gitignore @@ -0,0 +1,127 @@ +*.env +build +coverage +html-report +build +../.DS_Store +# Created by https://www.gitignore.io/api/osx,windows,vim,node,linux + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon +# Thumbnails +._* +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### Windows ### +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + + +### Vim ### +# swap +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags + + +### Node ### +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + + +### Linux ### + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* diff --git a/lab-raziyeh/README.md b/lab-raziyeh/README.md new file mode 100644 index 0000000..863ff00 --- /dev/null +++ b/lab-raziyeh/README.md @@ -0,0 +1,4 @@ +# SLUGRAM BACKEND +[![Build Status](https://travis-ci.org/slugbyte/slugram-backend.svg?branch=stageing)](https://travis-ci.org/slugbyte/slugram-backend) +[![Coverage Status](https://coveralls.io/repos/github/slugbyte/slugram-backend/badge.svg?branch=stageing)](https://coveralls.io/github/slugbyte/slugram-backend?branch=stageing) + diff --git a/lab-raziyeh/app/component/gallery/create-gallery/_create-gallery.scss b/lab-raziyeh/app/component/gallery/create-gallery/_create-gallery.scss new file mode 100644 index 0000000..e69de29 diff --git a/lab-raziyeh/app/component/gallery/create-gallery/create-gallery.html b/lab-raziyeh/app/component/gallery/create-gallery/create-gallery.html new file mode 100644 index 0000000..2efab57 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/create-gallery/create-gallery.html @@ -0,0 +1,21 @@ + +
+
+ +
+ +
+ +
+ + +
+ diff --git a/lab-raziyeh/app/component/gallery/create-gallery/create-gallery.js b/lab-raziyeh/app/component/gallery/create-gallery/create-gallery.js new file mode 100644 index 0000000..3484c83 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/create-gallery/create-gallery.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = { + template: require('./create-gallery.html'), + controller: ['$log', 'galleryService', CreateGalleryController], + controllerAs: 'createGalleryCtrl', +}; + +function CreateGalleryController($log, galleryService){ + $log.debug('init createGalleryCtrl'); + this.gallery = {}; + + this.createGallery = function(){ + galleryService.createGallery(this.gallery) + .then(() => { + this.gallery.name = null; + this.gallery.desc = null; + }); + }; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/edit-gallery/edit-gallery.html b/lab-raziyeh/app/component/gallery/edit-gallery/edit-gallery.html new file mode 100644 index 0000000..b5b6e20 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/edit-gallery/edit-gallery.html @@ -0,0 +1,5 @@ +
+ + + +
\ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/edit-gallery/edit-gallery.js b/lab-raziyeh/app/component/gallery/edit-gallery/edit-gallery.js new file mode 100644 index 0000000..3de9fdf --- /dev/null +++ b/lab-raziyeh/app/component/gallery/edit-gallery/edit-gallery.js @@ -0,0 +1,21 @@ +'use strict'; + +module.exports = { + template: require('./edit-gallery.html'), + controller: ['$log', 'galleryService', EditGalleryController], + controllerAs: 'editGalleryCtrl', + bindings: { + gallery: '<', + }, +}; + +function EditGalleryController($log, galleryService){ + $log.debug('init editGalleryCtrl'); + + this.updateGallery = function(){ + galleryService.updateGallery(this.gallery._id, this.gallery) + .catch( () => { + $log.debug('error in editGalleryCtrl'); + }); + }; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/gallery-items/gallery-items.html b/lab-raziyeh/app/component/gallery/gallery-items/gallery-items.html new file mode 100644 index 0000000..b679889 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/gallery-items/gallery-items.html @@ -0,0 +1,14 @@ + + + {{ galleryItemsCtrl.gallery.name | uppercase }} - {{ galleryItemsCtrl.gallery.desc }} + + + + + + + \ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/gallery-items/gallery-items.js b/lab-raziyeh/app/component/gallery/gallery-items/gallery-items.js new file mode 100644 index 0000000..7df47ce --- /dev/null +++ b/lab-raziyeh/app/component/gallery/gallery-items/gallery-items.js @@ -0,0 +1,27 @@ +'use strict'; + +module.exports = { + template: require('./gallery-items.html'), + controller: ['$log','galleryService', GalleryItemsController], + controllerAs: 'galleryItemsCtrl', + bindings: { + gallery: '<', + deleteDone: '&', + }, +}; + +function GalleryItemsController($log, galleryService){ + $log.debug('init galleryItemsCtrl'); + this.showEdit = false; + + this.deleteGallery = function(){ + galleryService.deleteGallery(this.gallery._id) + .then( () => { + $log.debug('delete gallery by Is, Done!'); + this.deleteDone({galleryData: this.gallery}); + }) + .catch(err => { + $log.debug('delete gallery failed', err.message); + }); + }; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/thumbnail-container/_thumbnail-container.scss b/lab-raziyeh/app/component/gallery/thumbnail-container/_thumbnail-container.scss new file mode 100644 index 0000000..94a7bd8 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/thumbnail-container/_thumbnail-container.scss @@ -0,0 +1,3 @@ +.home .panel-heading { + background: #f5f5f5; +}; \ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/thumbnail-container/thumbnail-container.html b/lab-raziyeh/app/component/gallery/thumbnail-container/thumbnail-container.html new file mode 100644 index 0000000..3f97b5d --- /dev/null +++ b/lab-raziyeh/app/component/gallery/thumbnail-container/thumbnail-container.html @@ -0,0 +1,12 @@ + +
+
+
Upload Photo
+
+
+
{{ thumbnailContainerCtrl.gallery.name }}
+ + +
+
+ diff --git a/lab-raziyeh/app/component/gallery/thumbnail-container/thumbnail-container.js b/lab-raziyeh/app/component/gallery/thumbnail-container/thumbnail-container.js new file mode 100644 index 0000000..b7bf770 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/thumbnail-container/thumbnail-container.js @@ -0,0 +1,11 @@ +'use strict'; + +require('./_thumbnail-container.scss'); + +module.exports = { + template: require('./thumbnail-container.html'), + controllerAs: 'thumbnailContainerCtrl', + bindings: { + gallery: '<', + }, +}; \ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/thumbnail/thumbnail.html b/lab-raziyeh/app/component/gallery/thumbnail/thumbnail.html new file mode 100644 index 0000000..40cf3f2 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/thumbnail/thumbnail.html @@ -0,0 +1,14 @@ + +
+
+
+ + {{thumbnailCtrl.pic.desc}} + +
+
+
{{thumbnailCtrl.pic.name}}
+

{{thumbnailCtrl.pic.desc}}

+ +
+
\ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/thumbnail/thumbnail.js b/lab-raziyeh/app/component/gallery/thumbnail/thumbnail.js new file mode 100644 index 0000000..db6a486 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/thumbnail/thumbnail.js @@ -0,0 +1,26 @@ +'use strict'; + +module.exports = { + template: require('./thumbnail.html'), + controllerAs: 'thumbnailCtrl', + controller: ['$log', 'picService', ThumbnailController], + bindings: { + pic: '<', + gallery: '<', + }, +}; + +function ThumbnailController($log, picService){ + $log.debug('init picService'); + + this.deletePic = function(){ + $log.debug('thumbnailCtrl.deletePic()'); + picService.deleteGalleryPic(this.gallery, this.pic) + .then(() => { + $log.debug('delete pic, Done!'); + }) + .catch(() => { + $log.debug('delete pic by has error!'); + }); + }; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/upload-pic/upload-pic.html b/lab-raziyeh/app/component/gallery/upload-pic/upload-pic.html new file mode 100644 index 0000000..f6b1c40 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/upload-pic/upload-pic.html @@ -0,0 +1,16 @@ + +
+
+ + + + +
+ Select Image +
+ + +
+
\ No newline at end of file diff --git a/lab-raziyeh/app/component/gallery/upload-pic/upload-pic.js b/lab-raziyeh/app/component/gallery/upload-pic/upload-pic.js new file mode 100644 index 0000000..5500061 --- /dev/null +++ b/lab-raziyeh/app/component/gallery/upload-pic/upload-pic.js @@ -0,0 +1,24 @@ + +'use strict'; + +module.exports = { + controllerAs: 'uploadPicCtrl', + template: require('./upload-pic.html'), + controller: ['$log', 'picService', UploadPicController], + bindings: { + gallery: '<', + }, +}; + +function UploadPicController($log, picService){ + $log.debug('init uploadPicCtrl'); + this.pic = {}, + this.uploadPic = function(){ + picService.uploadGalleryPic(this.gallery, this.pic) + .then(() => { + this.pic.name = null; + this.pic.desc = null; + this.pic.file = null; + }); + }; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/navbar/_navbar.scss b/lab-raziyeh/app/component/navbar/_navbar.scss new file mode 100644 index 0000000..eac643a --- /dev/null +++ b/lab-raziyeh/app/component/navbar/_navbar.scss @@ -0,0 +1,21 @@ +@import "_theme"; + +.landing { + background-color: $app-secondary; +} + +.navbar-inverse { + background-color: #6f94a2 !important; + border-color: #6f94a2 !important; +} +.navbar-inverse a { + color:#fff!important; + font-weight: 400; +} +.navbar-nav > li > a { + padding-top:10px !important; + padding-bottom:10px !important; +} +img { + max-width: 100px; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/navbar/navbar.html b/lab-raziyeh/app/component/navbar/navbar.html new file mode 100644 index 0000000..9308764 --- /dev/null +++ b/lab-raziyeh/app/component/navbar/navbar.html @@ -0,0 +1,23 @@ + + diff --git a/lab-raziyeh/app/component/navbar/navbar.js b/lab-raziyeh/app/component/navbar/navbar.js new file mode 100644 index 0000000..42a0a0c --- /dev/null +++ b/lab-raziyeh/app/component/navbar/navbar.js @@ -0,0 +1,30 @@ +'use strict'; + +module.exports = { + template: require('./navbar.html'), + controller: ['$log','$location', NavbarController], + controllerAs: 'navbarCtrl', +}; + +function NavbarController($log, $location){ + $log.debug('navbar component'); + + this.btnTitle = 'Sign in'; + if ($location.url() === '/home') { + this.btnTitle = 'Log out'; + this.showLogout = true; + } + + this.signin = function() { + $location.url('/login'); + }; + + let googleAuthBase = 'https://accounts.google.com/o/oauth2/v2/auth'; + let googleAuthResponseType = 'response_type=code'; + let googleAuthClientID = `client_id=${__GOOGLE_CLIENT_ID__}`; + let googleAuthScope = 'scope=profile%20email%20openid'; + let googleAuthRedirectURI = 'redirect_uri=http://localhost:3000/oauth/signout-api'; + let googleAuthAccessType = 'access_type=offline'; + + this.googleAuthURL = `${googleAuthBase}?${googleAuthResponseType}&${googleAuthClientID}&${googleAuthScope}&${googleAuthRedirectURI}&${googleAuthAccessType}`; +} diff --git a/lab-raziyeh/app/component/searchbar/_searchbar.scss b/lab-raziyeh/app/component/searchbar/_searchbar.scss new file mode 100644 index 0000000..e69de29 diff --git a/lab-raziyeh/app/component/searchbar/searchbar.html b/lab-raziyeh/app/component/searchbar/searchbar.html new file mode 100644 index 0000000..f871d91 --- /dev/null +++ b/lab-raziyeh/app/component/searchbar/searchbar.html @@ -0,0 +1,29 @@ +
+
+
+ + +
+ + + + + + + +
+
+
\ No newline at end of file diff --git a/lab-raziyeh/app/component/searchbar/searchbar.js b/lab-raziyeh/app/component/searchbar/searchbar.js new file mode 100644 index 0000000..e175f7c --- /dev/null +++ b/lab-raziyeh/app/component/searchbar/searchbar.js @@ -0,0 +1,36 @@ +'use strict'; + +module.exports = { + template: require('./searchbar.html'), + controller: ['$log', SearchbarController], + controllerAs: 'searchbarCtrl', + bindings: { + searchTerm: '=', + fieldSearch: '=', + }, +}; + +function SearchbarController($log){ + $log.debug('searchbarCtrl component'); + + this.fieldSearch = 'name'; + this.activeName = true; + this.activeDesc = false; + + this.reset = function() { + this.searchTerm = null; + this.searchtext = null; + }; + + this.makeActive = function(value) { + if(value === 'desc') { + this.activeName = false; + this.activeDesc = true; + this.fieldSearch = 'desc'; + return; + } + this.activeName = true; + this.activeDesc = false; + this.fieldSearch = 'name'; + }; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/signin/_signin.scss b/lab-raziyeh/app/component/signin/_signin.scss new file mode 100644 index 0000000..6a08eb3 --- /dev/null +++ b/lab-raziyeh/app/component/signin/_signin.scss @@ -0,0 +1,5 @@ +@import "theme"; + +.signin { + background-color: $app-secondary; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/signin/signin.html b/lab-raziyeh/app/component/signin/signin.html new file mode 100644 index 0000000..a2bb8f3 --- /dev/null +++ b/lab-raziyeh/app/component/signin/signin.html @@ -0,0 +1,32 @@ + +
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
+
+
+ +
+
+
+
+
+
+
diff --git a/lab-raziyeh/app/component/signin/signin.js b/lab-raziyeh/app/component/signin/signin.js new file mode 100644 index 0000000..f05d147 --- /dev/null +++ b/lab-raziyeh/app/component/signin/signin.js @@ -0,0 +1,19 @@ +'use strict'; + +module.exports = { + template: require('./signin.html'), + controller: ['$log', '$location', 'authService', SigninController], + controllerAs: 'signinCtrl', +}; + +function SigninController($log, $location, authService){ + this.signin = function(user){ + authService.login(user) + .then(() => { + $location.path('/home'); + }) + .catch(() => { + console.log('faild to signin'); + }); + }; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/signup/_signup.scss b/lab-raziyeh/app/component/signup/_signup.scss new file mode 100644 index 0000000..14ab455 --- /dev/null +++ b/lab-raziyeh/app/component/signup/_signup.scss @@ -0,0 +1,5 @@ +@import "theme"; + +.signup { + background-color: $app-secondary; +} \ No newline at end of file diff --git a/lab-raziyeh/app/component/signup/signup.html b/lab-raziyeh/app/component/signup/signup.html new file mode 100644 index 0000000..904e28c --- /dev/null +++ b/lab-raziyeh/app/component/signup/signup.html @@ -0,0 +1,37 @@ + +
+
+
+
+
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+
+ +
+
+
+
+
+
+
diff --git a/lab-raziyeh/app/component/signup/signup.js b/lab-raziyeh/app/component/signup/signup.js new file mode 100644 index 0000000..7becd76 --- /dev/null +++ b/lab-raziyeh/app/component/signup/signup.js @@ -0,0 +1,19 @@ +'use strict'; + +module.exports = { + template: require('./signup.html'), + controller: ['$log', '$location', 'authService', SignupController], + controllerAs: 'signupCtrl', +}; + +function SignupController($log, $location, authService){ + this.signup = function(user){ + authService.signup(user) + .then(() => { + $location.path('/home'); + }) + .catch(() => { + console.log('faild to signup'); + }); + }; +} \ No newline at end of file diff --git a/lab-raziyeh/app/config/log-config.js b/lab-raziyeh/app/config/log-config.js new file mode 100644 index 0000000..9ffc30a --- /dev/null +++ b/lab-raziyeh/app/config/log-config.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = ['$logProvider', logConfig]; + +function logConfig($logProvider){ + $logProvider.debugEnabled(__DEBUG__); +} \ No newline at end of file diff --git a/lab-raziyeh/app/config/router-config.js b/lab-raziyeh/app/config/router-config.js new file mode 100644 index 0000000..a955370 --- /dev/null +++ b/lab-raziyeh/app/config/router-config.js @@ -0,0 +1,30 @@ +'use strict'; + +module.exports = ['$stateProvider', '$urlRouterProvider', routerConfig]; + +function routerConfig($stateProvider, $urlRouterProvider){ + + $stateProvider + .state('home', { + url: '/home', + controllerAs: 'homeCtrl', + controller: 'HomeController', + template: require('../view/home/home.html'), + }) + + .state('welcome', { + url: '/', + controllerAs: 'landingCtrl', + controller: 'LandingController', + template: require('../view/landing/landing.html'), + }) + + .state('login', { + url: '/login', + controllerAs: 'loginCtrl', + controller: 'LoginController', + template: require('../view/login/login.html'), + }); + + $urlRouterProvider.otherwise('/'); +} diff --git a/lab-raziyeh/app/data/background.png b/lab-raziyeh/app/data/background.png new file mode 100644 index 0000000..18caec7 Binary files /dev/null and b/lab-raziyeh/app/data/background.png differ diff --git a/lab-raziyeh/app/data/razi.jpg b/lab-raziyeh/app/data/razi.jpg new file mode 100644 index 0000000..b75ddc0 Binary files /dev/null and b/lab-raziyeh/app/data/razi.jpg differ diff --git a/lab-raziyeh/app/entry.js b/lab-raziyeh/app/entry.js new file mode 100644 index 0000000..6442b7e --- /dev/null +++ b/lab-raziyeh/app/entry.js @@ -0,0 +1,60 @@ +'use strict'; + +// build sass +require('./scss/main.scss'); + +// require node modules +const path = require('path'); + +// require npm modules +const angular = require('angular'); +const camelcase = require('camelcase'); +const pascalcase = require('pascalcase'); + +// require angualr modules +const ngTouch = require('angular-touch'); +const ngAnimate = require('angular-animate'); +const uiRouter = require('angular-ui-router'); +const ngFileUpload = require('ng-file-upload'); +const uiBootstrap = require('angular-ui-bootstrap'); + +// create angular module +const demoApp = angular.module('demoApp', [ngTouch, ngAnimate, uiRouter, uiBootstrap, ngFileUpload]); + +// load config +let context = require.context('./config/', true, /.js$/); +context.keys().forEach( path => { + demoApp.config(context(path)); +}); + +// load view controllers +context = require.context('./view/', true, /.js$/); +context.keys().forEach( key => { + let name = pascalcase(path.basename(key, '.js')); // name controller based on file name + let module = context(key); // value of module.exports + demoApp.controller(name, module); +}); + +// load services +context = require.context('./service/', true, /.js$/); +context.keys().forEach( key => { + let name = camelcase(path.basename(key, '.js')); + let module = context(key); // value of module.exports + demoApp.service(name, module); +}); + +// load components +context = require.context('./component/', true, /.js$/); +context.keys().forEach( key => { + let name = camelcase(path.basename(key, '.js')); + let module = context(key); // value of module.exports + demoApp.component(name, module); +}); + +// load filters +context = require.context('./filter/', true, /\.js$/); +context.keys().forEach( key => { + let name = camelcase(path.basename(key, '.js')); + let module = context(key); + demoApp.filter(name, module); +}); diff --git a/lab-raziyeh/app/filter/gallery-search.js b/lab-raziyeh/app/filter/gallery-search.js new file mode 100644 index 0000000..03d76b5 --- /dev/null +++ b/lab-raziyeh/app/filter/gallery-search.js @@ -0,0 +1,19 @@ +'use strict'; + +module.exports = function(){ + return function(galleries, searchTerm, fieldSearch){ + let fuzzyRegex = genorateFuzzyRegex(searchTerm); + + return galleries.filter(gallery => { + if(fieldSearch === 'name') + return fuzzyRegex.test(gallery.name.toUpperCase()); + return fuzzyRegex.test(gallery.desc.toUpperCase()); + }); + }; +}; + +function genorateFuzzyRegex(input){ + if (!input) return /.*/; + let fuzzyString = '.*' + input.toUpperCase().split('').join('.*') + '.*'; + return new RegExp(fuzzyString); +} \ No newline at end of file diff --git a/lab-raziyeh/app/image/codefellows.png b/lab-raziyeh/app/image/codefellows.png new file mode 100644 index 0000000..d66eb56 Binary files /dev/null and b/lab-raziyeh/app/image/codefellows.png differ diff --git a/lab-raziyeh/app/index.html b/lab-raziyeh/app/index.html new file mode 100644 index 0000000..dbd09be --- /dev/null +++ b/lab-raziyeh/app/index.html @@ -0,0 +1,12 @@ + + + + Example Title + + + + + + + + \ No newline at end of file diff --git a/lab-raziyeh/app/scss/lib/_custom-bootstrap-variables.scss b/lab-raziyeh/app/scss/lib/_custom-bootstrap-variables.scss new file mode 100644 index 0000000..f73962b --- /dev/null +++ b/lab-raziyeh/app/scss/lib/_custom-bootstrap-variables.scss @@ -0,0 +1,876 @@ +@import "theme"; + +$bootstrap-sass-asset-helper: false !default; +// +// Variables +// -------------------------------------------------- + + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +$gray-base: $app-black !default; +$gray-darker: lighten($gray-base, 13.5%) !default; // #222 +$gray-dark: lighten($gray-base, 20%) !default; // #333 +$gray: lighten($gray-base, 33.5%) !default; // #555 +$gray-light: lighten($gray-base, 46.7%) !default; // #777 +$gray-lighter: lighten($gray-base, 93.5%) !default; // #eee + +$brand-primary: darken($app-primary, 6.5%) !default; +$brand-success: $app-success !default; +$brand-info: $app-info !default; +$brand-warning: $app-warn !default; +$brand-danger: $app-error !default; + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for ``. +$body-bg: $app-white !default; +//** Global text color on ``. +$text-color: $gray-dark !default; + +//** Global textual link color. +$link-color: $brand-primary !default; +//** Link hover color set via `darken()` function. +$link-hover-color: darken($link-color, 15%) !default; +//** Link hover decoration. +$link-hover-decoration: underline !default; + + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; +$font-family-serif: Georgia, "Times New Roman", Times, serif !default; +//** Default monospace fonts for ``, ``, and `
`.
+$font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace !default;
+$font-family-base:        $font-family-sans-serif !default;
+
+$font-size-base:          14px !default;
+$font-size-large:         ceil(($font-size-base * 1.25)) !default; // ~18px
+$font-size-small:         ceil(($font-size-base * 0.85)) !default; // ~12px
+
+$font-size-h1:            floor(($font-size-base * 2.6)) !default; // ~36px
+$font-size-h2:            floor(($font-size-base * 2.15)) !default; // ~30px
+$font-size-h3:            ceil(($font-size-base * 1.7)) !default; // ~24px
+$font-size-h4:            ceil(($font-size-base * 1.25)) !default; // ~18px
+$font-size-h5:            $font-size-base !default;
+$font-size-h6:            ceil(($font-size-base * 0.85)) !default; // ~12px
+
+//** Unit-less `line-height` for use in components like buttons.
+$line-height-base:        1.428571429 !default; // 20/14
+//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
+$line-height-computed:    floor(($font-size-base * $line-height-base)) !default; // ~20px
+
+//** By default, this inherits from the ``.
+$headings-font-family:    inherit !default;
+$headings-font-weight:    500 !default;
+$headings-line-height:    1.1 !default;
+$headings-color:          inherit !default;
+
+
+//== Iconography
+//
+//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
+
+//** Load fonts from this directory.
+
+// [converter] If $bootstrap-sass-asset-helper if used, provide path relative to the assets load path.
+// [converter] This is because some asset helpers, such as Sprockets, do not work with file-relative paths.
+$icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/") !default;
+
+//** File name for all font files.
+$icon-font-name:          "glyphicons-halflings-regular" !default;
+//** Element ID within SVG icon file.
+$icon-font-svg-id:        "glyphicons_halflingsregular" !default;
+
+
+//== Components
+//
+//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
+
+$padding-base-vertical:     6px !default;
+$padding-base-horizontal:   12px !default;
+
+$padding-large-vertical:    10px !default;
+$padding-large-horizontal:  16px !default;
+
+$padding-small-vertical:    5px !default;
+$padding-small-horizontal:  10px !default;
+
+$padding-xs-vertical:       1px !default;
+$padding-xs-horizontal:     5px !default;
+
+$line-height-large:         1.3333333 !default; // extra decimals for Win 8.1 Chrome
+$line-height-small:         1.5 !default;
+
+$border-radius-base:        4px !default;
+$border-radius-large:       6px !default;
+$border-radius-small:       3px !default;
+
+//** Global color for active items (e.g., navs or dropdowns).
+$component-active-color:    #fff !default;
+//** Global background color for active items (e.g., navs or dropdowns).
+$component-active-bg:       $brand-primary !default;
+
+//** Width of the `border` for generating carets that indicate dropdowns.
+$caret-width-base:          4px !default;
+//** Carets increase slightly in size for larger components.
+$caret-width-large:         5px !default;
+
+
+//== Tables
+//
+//## Customizes the `.table` component with basic values, each used across all table variations.
+
+//** Padding for ``s and ``s.
+$table-cell-padding:            8px !default;
+//** Padding for cells in `.table-condensed`.
+$table-condensed-cell-padding:  5px !default;
+
+//** Default background color used for all tables.
+$table-bg:                      transparent !default;
+//** Background color used for `.table-striped`.
+$table-bg-accent:               #f9f9f9 !default;
+//** Background color used for `.table-hover`.
+$table-bg-hover:                #f5f5f5 !default;
+$table-bg-active:               $table-bg-hover !default;
+
+//** Border color for table and cell borders.
+$table-border-color:            #ddd !default;
+
+
+//== Buttons
+//
+//## For each of Bootstrap's buttons, define text, background and border color.
+
+$btn-font-weight:                normal !default;
+
+$btn-default-color:              #333 !default;
+$btn-default-bg:                 #fff !default;
+$btn-default-border:             #ccc !default;
+
+$btn-primary-color:              #fff !default;
+$btn-primary-bg:                 $brand-primary !default;
+$btn-primary-border:             darken($btn-primary-bg, 5%) !default;
+
+$btn-success-color:              #fff !default;
+$btn-success-bg:                 $brand-success !default;
+$btn-success-border:             darken($btn-success-bg, 5%) !default;
+
+$btn-info-color:                 #fff !default;
+$btn-info-bg:                    $brand-info !default;
+$btn-info-border:                darken($btn-info-bg, 5%) !default;
+
+$btn-warning-color:              #fff !default;
+$btn-warning-bg:                 $brand-warning !default;
+$btn-warning-border:             darken($btn-warning-bg, 5%) !default;
+
+$btn-danger-color:               #fff !default;
+$btn-danger-bg:                  $brand-danger !default;
+$btn-danger-border:              darken($btn-danger-bg, 5%) !default;
+
+$btn-link-disabled-color:        $gray-light !default;
+
+// Allows for customizing button radius independently from global border radius
+$btn-border-radius-base:         $border-radius-base !default;
+$btn-border-radius-large:        $border-radius-large !default;
+$btn-border-radius-small:        $border-radius-small !default;
+
+
+//== Forms
+//
+//##
+
+//** `` background color
+$input-bg:                       #fff !default;
+//** `` background color
+$input-bg-disabled:              $gray-lighter !default;
+
+//** Text color for ``s
+$input-color:                    $gray !default;
+//** `` border color
+$input-border:                   #ccc !default;
+
+// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
+//** Default `.form-control` border radius
+// This has no effect on ``s in CSS.
+$input-border-radius:            $border-radius-base !default;
+//** Large `.form-control` border radius
+$input-border-radius-large:      $border-radius-large !default;
+//** Small `.form-control` border radius
+$input-border-radius-small:      $border-radius-small !default;
+
+//** Border color for inputs on focus
+$input-border-focus:             #66afe9 !default;
+
+//** Placeholder text color
+$input-color-placeholder:        #999 !default;
+
+//** Default `.form-control` height
+$input-height-base:              ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
+//** Large `.form-control` height
+$input-height-large:             (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
+//** Small `.form-control` height
+$input-height-small:             (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
+
+//** `.form-group` margin
+$form-group-margin-bottom:       15px !default;
+
+$legend-color:                   $gray-dark !default;
+$legend-border-color:            #e5e5e5 !default;
+
+//** Background color for textual input addons
+$input-group-addon-bg:           $gray-lighter !default;
+//** Border color for textual input addons
+$input-group-addon-border-color: $input-border !default;
+
+//** Disabled cursor for form controls and buttons.
+$cursor-disabled:                not-allowed !default;
+
+
+//== Dropdowns
+//
+//## Dropdown menu container and contents.
+
+//** Background for the dropdown menu.
+$dropdown-bg:                    #fff !default;
+//** Dropdown menu `border-color`.
+$dropdown-border:                rgba(0,0,0,.15) !default;
+//** Dropdown menu `border-color` **for IE8**.
+$dropdown-fallback-border:       #ccc !default;
+//** Divider color for between dropdown items.
+$dropdown-divider-bg:            #e5e5e5 !default;
+
+//** Dropdown link text color.
+$dropdown-link-color:            $gray-dark !default;
+//** Hover color for dropdown links.
+$dropdown-link-hover-color:      darken($gray-dark, 5%) !default;
+//** Hover background for dropdown links.
+$dropdown-link-hover-bg:         #f5f5f5 !default;
+
+//** Active dropdown menu item text color.
+$dropdown-link-active-color:     $component-active-color !default;
+//** Active dropdown menu item background color.
+$dropdown-link-active-bg:        $component-active-bg !default;
+
+//** Disabled dropdown menu item background color.
+$dropdown-link-disabled-color:   $gray-light !default;
+
+//** Text color for headers within dropdown menus.
+$dropdown-header-color:          $gray-light !default;
+
+//** Deprecated `$dropdown-caret-color` as of v3.1.0
+$dropdown-caret-color:           #000 !default;
+
+
+//-- Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+//
+// Note: These variables are not generated into the Customizer.
+
+$zindex-navbar:            1000 !default;
+$zindex-dropdown:          1000 !default;
+$zindex-popover:           1060 !default;
+$zindex-tooltip:           1070 !default;
+$zindex-navbar-fixed:      1030 !default;
+$zindex-modal-background:  1040 !default;
+$zindex-modal:             1050 !default;
+
+
+//== Media queries breakpoints
+//
+//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
+
+// Extra small screen / phone
+//** Deprecated `$screen-xs` as of v3.0.1
+$screen-xs:                  480px !default;
+//** Deprecated `$screen-xs-min` as of v3.2.0
+$screen-xs-min:              $screen-xs !default;
+//** Deprecated `$screen-phone` as of v3.0.1
+$screen-phone:               $screen-xs-min !default;
+
+// Small screen / tablet
+//** Deprecated `$screen-sm` as of v3.0.1
+$screen-sm:                  768px !default;
+$screen-sm-min:              $screen-sm !default;
+//** Deprecated `$screen-tablet` as of v3.0.1
+$screen-tablet:              $screen-sm-min !default;
+
+// Medium screen / desktop
+//** Deprecated `$screen-md` as of v3.0.1
+$screen-md:                  992px !default;
+$screen-md-min:              $screen-md !default;
+//** Deprecated `$screen-desktop` as of v3.0.1
+$screen-desktop:             $screen-md-min !default;
+
+// Large screen / wide desktop
+//** Deprecated `$screen-lg` as of v3.0.1
+$screen-lg:                  1200px !default;
+$screen-lg-min:              $screen-lg !default;
+//** Deprecated `$screen-lg-desktop` as of v3.0.1
+$screen-lg-desktop:          $screen-lg-min !default;
+
+// So media queries don't overlap when required, provide a maximum
+$screen-xs-max:              ($screen-sm-min - 1) !default;
+$screen-sm-max:              ($screen-md-min - 1) !default;
+$screen-md-max:              ($screen-lg-min - 1) !default;
+
+
+//== Grid system
+//
+//## Define your custom responsive grid.
+
+//** Number of columns in the grid.
+$grid-columns:              12 !default;
+//** Padding between columns. Gets divided in half for the left and right.
+$grid-gutter-width:         30px !default;
+// Navbar collapse
+//** Point at which the navbar becomes uncollapsed.
+$grid-float-breakpoint:     $screen-sm-min !default;
+//** Point at which the navbar begins collapsing.
+$grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
+
+
+//== Container sizes
+//
+//## Define the maximum width of `.container` for different screen sizes.
+
+// Small screen / tablet
+$container-tablet:             (720px + $grid-gutter-width) !default;
+//** For `$screen-sm-min` and up.
+$container-sm:                 $container-tablet !default;
+
+// Medium screen / desktop
+$container-desktop:            (940px + $grid-gutter-width) !default;
+//** For `$screen-md-min` and up.
+$container-md:                 $container-desktop !default;
+
+// Large screen / wide desktop
+$container-large-desktop:      (1140px + $grid-gutter-width) !default;
+//** For `$screen-lg-min` and up.
+$container-lg:                 $container-large-desktop !default;
+
+
+//== Navbar
+//
+//##
+
+// Basics of a navbar
+$navbar-height:                    50px !default;
+$navbar-margin-bottom:             $line-height-computed !default;
+$navbar-border-radius:             $border-radius-base !default;
+$navbar-padding-horizontal:        floor(($grid-gutter-width / 2)) !default;
+$navbar-padding-vertical:          (($navbar-height - $line-height-computed) / 2) !default;
+$navbar-collapse-max-height:       340px !default;
+
+$navbar-default-color:             #777 !default;
+$navbar-default-bg:                #f8f8f8 !default;
+$navbar-default-border:            darken($navbar-default-bg, 6.5%) !default;
+
+// Navbar links
+$navbar-default-link-color:                #777 !default;
+$navbar-default-link-hover-color:          #333 !default;
+$navbar-default-link-hover-bg:             transparent !default;
+$navbar-default-link-active-color:         #555 !default;
+$navbar-default-link-active-bg:            darken($navbar-default-bg, 6.5%) !default;
+$navbar-default-link-disabled-color:       #ccc !default;
+$navbar-default-link-disabled-bg:          transparent !default;
+
+// Navbar brand label
+$navbar-default-brand-color:               $navbar-default-link-color !default;
+$navbar-default-brand-hover-color:         darken($navbar-default-brand-color, 10%) !default;
+$navbar-default-brand-hover-bg:            transparent !default;
+
+// Navbar toggle
+$navbar-default-toggle-hover-bg:           #ddd !default;
+$navbar-default-toggle-icon-bar-bg:        #888 !default;
+$navbar-default-toggle-border-color:       #ddd !default;
+
+
+//=== Inverted navbar
+// Reset inverted navbar basics
+$navbar-inverse-color:                      lighten($gray-light, 15%) !default;
+$navbar-inverse-bg:                         #222 !default;
+$navbar-inverse-border:                     darken($navbar-inverse-bg, 10%) !default;
+
+// Inverted navbar links
+$navbar-inverse-link-color:                 lighten($gray-light, 15%) !default;
+$navbar-inverse-link-hover-color:           #fff !default;
+$navbar-inverse-link-hover-bg:              transparent !default;
+$navbar-inverse-link-active-color:          $navbar-inverse-link-hover-color !default;
+$navbar-inverse-link-active-bg:             darken($navbar-inverse-bg, 10%) !default;
+$navbar-inverse-link-disabled-color:        #444 !default;
+$navbar-inverse-link-disabled-bg:           transparent !default;
+
+// Inverted navbar brand label
+$navbar-inverse-brand-color:                $navbar-inverse-link-color !default;
+$navbar-inverse-brand-hover-color:          #fff !default;
+$navbar-inverse-brand-hover-bg:             transparent !default;
+
+// Inverted navbar toggle
+$navbar-inverse-toggle-hover-bg:            #333 !default;
+$navbar-inverse-toggle-icon-bar-bg:         #fff !default;
+$navbar-inverse-toggle-border-color:        #333 !default;
+
+
+//== Navs
+//
+//##
+
+//=== Shared nav styles
+$nav-link-padding:                          10px 15px !default;
+$nav-link-hover-bg:                         $gray-lighter !default;
+
+$nav-disabled-link-color:                   $gray-light !default;
+$nav-disabled-link-hover-color:             $gray-light !default;
+
+//== Tabs
+$nav-tabs-border-color:                     #ddd !default;
+
+$nav-tabs-link-hover-border-color:          $gray-lighter !default;
+
+$nav-tabs-active-link-hover-bg:             $body-bg !default;
+$nav-tabs-active-link-hover-color:          $gray !default;
+$nav-tabs-active-link-hover-border-color:   #ddd !default;
+
+$nav-tabs-justified-link-border-color:            #ddd !default;
+$nav-tabs-justified-active-link-border-color:     $body-bg !default;
+
+//== Pills
+$nav-pills-border-radius:                   $border-radius-base !default;
+$nav-pills-active-link-hover-bg:            $component-active-bg !default;
+$nav-pills-active-link-hover-color:         $component-active-color !default;
+
+
+//== Pagination
+//
+//##
+
+$pagination-color:                     $link-color !default;
+$pagination-bg:                        #fff !default;
+$pagination-border:                    #ddd !default;
+
+$pagination-hover-color:               $link-hover-color !default;
+$pagination-hover-bg:                  $gray-lighter !default;
+$pagination-hover-border:              #ddd !default;
+
+$pagination-active-color:              #fff !default;
+$pagination-active-bg:                 $brand-primary !default;
+$pagination-active-border:             $brand-primary !default;
+
+$pagination-disabled-color:            $gray-light !default;
+$pagination-disabled-bg:               #fff !default;
+$pagination-disabled-border:           #ddd !default;
+
+
+//== Pager
+//
+//##
+
+$pager-bg:                             $pagination-bg !default;
+$pager-border:                         $pagination-border !default;
+$pager-border-radius:                  15px !default;
+
+$pager-hover-bg:                       $pagination-hover-bg !default;
+
+$pager-active-bg:                      $pagination-active-bg !default;
+$pager-active-color:                   $pagination-active-color !default;
+
+$pager-disabled-color:                 $pagination-disabled-color !default;
+
+
+//== Jumbotron
+//
+//##
+
+$jumbotron-padding:              30px !default;
+$jumbotron-color:                inherit !default;
+$jumbotron-bg:                   $gray-lighter !default;
+$jumbotron-heading-color:        inherit !default;
+$jumbotron-font-size:            ceil(($font-size-base * 1.5)) !default;
+$jumbotron-heading-font-size:    ceil(($font-size-base * 4.5)) !default;
+
+
+//== Form states and alerts
+//
+//## Define colors for form feedback states and, by default, alerts.
+
+$state-success-text:             #3c763d !default;
+$state-success-bg:               #dff0d8 !default;
+$state-success-border:           darken(adjust-hue($state-success-bg, -10), 5%) !default;
+
+$state-info-text:                #31708f !default;
+$state-info-bg:                  #d9edf7 !default;
+$state-info-border:              darken(adjust-hue($state-info-bg, -10), 7%) !default;
+
+$state-warning-text:             #8a6d3b !default;
+$state-warning-bg:               #fcf8e3 !default;
+$state-warning-border:           darken(adjust-hue($state-warning-bg, -10), 5%) !default;
+
+$state-danger-text:              #a94442 !default;
+$state-danger-bg:                #f2dede !default;
+$state-danger-border:            darken(adjust-hue($state-danger-bg, -10), 5%) !default;
+
+
+//== Tooltips
+//
+//##
+
+//** Tooltip max width
+$tooltip-max-width:           200px !default;
+//** Tooltip text color
+$tooltip-color:               #fff !default;
+//** Tooltip background color
+$tooltip-bg:                  #000 !default;
+$tooltip-opacity:             .9 !default;
+
+//** Tooltip arrow width
+$tooltip-arrow-width:         5px !default;
+//** Tooltip arrow color
+$tooltip-arrow-color:         $tooltip-bg !default;
+
+
+//== Popovers
+//
+//##
+
+//** Popover body background color
+$popover-bg:                          #fff !default;
+//** Popover maximum width
+$popover-max-width:                   276px !default;
+//** Popover border color
+$popover-border-color:                rgba(0,0,0,.2) !default;
+//** Popover fallback border color
+$popover-fallback-border-color:       #ccc !default;
+
+//** Popover title background color
+$popover-title-bg:                    darken($popover-bg, 3%) !default;
+
+//** Popover arrow width
+$popover-arrow-width:                 10px !default;
+//** Popover arrow color
+$popover-arrow-color:                 $popover-bg !default;
+
+//** Popover outer arrow width
+$popover-arrow-outer-width:           ($popover-arrow-width + 1) !default;
+//** Popover outer arrow color
+$popover-arrow-outer-color:           fade_in($popover-border-color, 0.05) !default;
+//** Popover outer arrow fallback color
+$popover-arrow-outer-fallback-color:  darken($popover-fallback-border-color, 20%) !default;
+
+
+//== Labels
+//
+//##
+
+//** Default label background color
+$label-default-bg:            $gray-light !default;
+//** Primary label background color
+$label-primary-bg:            $brand-primary !default;
+//** Success label background color
+$label-success-bg:            $brand-success !default;
+//** Info label background color
+$label-info-bg:               $brand-info !default;
+//** Warning label background color
+$label-warning-bg:            $brand-warning !default;
+//** Danger label background color
+$label-danger-bg:             $brand-danger !default;
+
+//** Default label text color
+$label-color:                 #fff !default;
+//** Default text color of a linked label
+$label-link-hover-color:      #fff !default;
+
+
+//== Modals
+//
+//##
+
+//** Padding applied to the modal body
+$modal-inner-padding:         15px !default;
+
+//** Padding applied to the modal title
+$modal-title-padding:         15px !default;
+//** Modal title line-height
+$modal-title-line-height:     $line-height-base !default;
+
+//** Background color of modal content area
+$modal-content-bg:                             #fff !default;
+//** Modal content border color
+$modal-content-border-color:                   rgba(0,0,0,.2) !default;
+//** Modal content border color **for IE8**
+$modal-content-fallback-border-color:          #999 !default;
+
+//** Modal backdrop background color
+$modal-backdrop-bg:           #000 !default;
+//** Modal backdrop opacity
+$modal-backdrop-opacity:      .5 !default;
+//** Modal header border color
+$modal-header-border-color:   #e5e5e5 !default;
+//** Modal footer border color
+$modal-footer-border-color:   $modal-header-border-color !default;
+
+$modal-lg:                    900px !default;
+$modal-md:                    600px !default;
+$modal-sm:                    300px !default;
+
+
+//== Alerts
+//
+//## Define alert colors, border radius, and padding.
+
+$alert-padding:               15px !default;
+$alert-border-radius:         $border-radius-base !default;
+$alert-link-font-weight:      bold !default;
+
+$alert-success-bg:            $state-success-bg !default;
+$alert-success-text:          $state-success-text !default;
+$alert-success-border:        $state-success-border !default;
+
+$alert-info-bg:               $state-info-bg !default;
+$alert-info-text:             $state-info-text !default;
+$alert-info-border:           $state-info-border !default;
+
+$alert-warning-bg:            $state-warning-bg !default;
+$alert-warning-text:          $state-warning-text !default;
+$alert-warning-border:        $state-warning-border !default;
+
+$alert-danger-bg:             $state-danger-bg !default;
+$alert-danger-text:           $state-danger-text !default;
+$alert-danger-border:         $state-danger-border !default;
+
+
+//== Progress bars
+//
+//##
+
+//** Background color of the whole progress component
+$progress-bg:                 #f5f5f5 !default;
+//** Progress bar text color
+$progress-bar-color:          #fff !default;
+//** Variable for setting rounded corners on progress bar.
+$progress-border-radius:      $border-radius-base !default;
+
+//** Default progress bar color
+$progress-bar-bg:             $brand-primary !default;
+//** Success progress bar color
+$progress-bar-success-bg:     $brand-success !default;
+//** Warning progress bar color
+$progress-bar-warning-bg:     $brand-warning !default;
+//** Danger progress bar color
+$progress-bar-danger-bg:      $brand-danger !default;
+//** Info progress bar color
+$progress-bar-info-bg:        $brand-info !default;
+
+
+//== List group
+//
+//##
+
+//** Background color on `.list-group-item`
+$list-group-bg:                 #fff !default;
+//** `.list-group-item` border color
+$list-group-border:             #ddd !default;
+//** List group border radius
+$list-group-border-radius:      $border-radius-base !default;
+
+//** Background color of single list items on hover
+$list-group-hover-bg:           #f5f5f5 !default;
+//** Text color of active list items
+$list-group-active-color:       $component-active-color !default;
+//** Background color of active list items
+$list-group-active-bg:          $component-active-bg !default;
+//** Border color of active list elements
+$list-group-active-border:      $list-group-active-bg !default;
+//** Text color for content within active list items
+$list-group-active-text-color:  lighten($list-group-active-bg, 40%) !default;
+
+//** Text color of disabled list items
+$list-group-disabled-color:      $gray-light !default;
+//** Background color of disabled list items
+$list-group-disabled-bg:         $gray-lighter !default;
+//** Text color for content within disabled list items
+$list-group-disabled-text-color: $list-group-disabled-color !default;
+
+$list-group-link-color:         #555 !default;
+$list-group-link-hover-color:   $list-group-link-color !default;
+$list-group-link-heading-color: #333 !default;
+
+
+//== Panels
+//
+//##
+
+$panel-bg:                    #fff !default;
+$panel-body-padding:          15px !default;
+$panel-heading-padding:       10px 15px !default;
+$panel-footer-padding:        $panel-heading-padding !default;
+$panel-border-radius:         $border-radius-base !default;
+
+//** Border color for elements within panels
+$panel-inner-border:          #ddd !default;
+$panel-footer-bg:             #f5f5f5 !default;
+
+$panel-default-text:          $gray-dark !default;
+$panel-default-border:        #ddd !default;
+$panel-default-heading-bg:    #f5f5f5 !default;
+
+$panel-primary-text:          #fff !default;
+$panel-primary-border:        $brand-primary !default;
+$panel-primary-heading-bg:    $brand-primary !default;
+
+$panel-success-text:          $state-success-text !default;
+$panel-success-border:        $state-success-border !default;
+$panel-success-heading-bg:    $state-success-bg !default;
+
+$panel-info-text:             $state-info-text !default;
+$panel-info-border:           $state-info-border !default;
+$panel-info-heading-bg:       $state-info-bg !default;
+
+$panel-warning-text:          $state-warning-text !default;
+$panel-warning-border:        $state-warning-border !default;
+$panel-warning-heading-bg:    $state-warning-bg !default;
+
+$panel-danger-text:           $state-danger-text !default;
+$panel-danger-border:         $state-danger-border !default;
+$panel-danger-heading-bg:     $state-danger-bg !default;
+
+
+//== Thumbnails
+//
+//##
+
+//** Padding around the thumbnail image
+$thumbnail-padding:           4px !default;
+//** Thumbnail background color
+$thumbnail-bg:                $body-bg !default;
+//** Thumbnail border color
+$thumbnail-border:            #ddd !default;
+//** Thumbnail border radius
+$thumbnail-border-radius:     $border-radius-base !default;
+
+//** Custom text color for thumbnail captions
+$thumbnail-caption-color:     $text-color !default;
+//** Padding around the thumbnail caption
+$thumbnail-caption-padding:   9px !default;
+
+
+//== Wells
+//
+//##
+
+$well-bg:                     #f5f5f5 !default;
+$well-border:                 darken($well-bg, 7%) !default;
+
+
+//== Badges
+//
+//##
+
+$badge-color:                 #fff !default;
+//** Linked badge text color on hover
+$badge-link-hover-color:      #fff !default;
+$badge-bg:                    $gray-light !default;
+
+//** Badge text color in active nav link
+$badge-active-color:          $link-color !default;
+//** Badge background color in active nav link
+$badge-active-bg:             #fff !default;
+
+$badge-font-weight:           bold !default;
+$badge-line-height:           1 !default;
+$badge-border-radius:         10px !default;
+
+
+//== Breadcrumbs
+//
+//##
+
+$breadcrumb-padding-vertical:   8px !default;
+$breadcrumb-padding-horizontal: 15px !default;
+//** Breadcrumb background color
+$breadcrumb-bg:                 #f5f5f5 !default;
+//** Breadcrumb text color
+$breadcrumb-color:              #ccc !default;
+//** Text color of current page in the breadcrumb
+$breadcrumb-active-color:       $gray-light !default;
+//** Textual separator for between breadcrumb elements
+$breadcrumb-separator:          "/" !default;
+
+
+//== Carousel
+//
+//##
+
+$carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6) !default;
+
+$carousel-control-color:                      #fff !default;
+$carousel-control-width:                      15% !default;
+$carousel-control-opacity:                    .5 !default;
+$carousel-control-font-size:                  20px !default;
+
+$carousel-indicator-active-bg:                #fff !default;
+$carousel-indicator-border-color:             #fff !default;
+
+$carousel-caption-color:                      #fff !default;
+
+
+//== Close
+//
+//##
+
+$close-font-weight:           bold !default;
+$close-color:                 #000 !default;
+$close-text-shadow:           0 1px 0 #fff !default;
+
+
+//== Code
+//
+//##
+
+$code-color:                  #c7254e !default;
+$code-bg:                     #f9f2f4 !default;
+
+$kbd-color:                   #fff !default;
+$kbd-bg:                      #333 !default;
+
+$pre-bg:                      #f5f5f5 !default;
+$pre-color:                   $gray-dark !default;
+$pre-border-color:            #ccc !default;
+$pre-scrollable-max-height:   340px !default;
+
+
+//== Type
+//
+//##
+
+//** Horizontal offset for forms and lists.
+$component-offset-horizontal: 180px !default;
+//** Text muted color
+$text-muted:                  $gray-light !default;
+//** Abbreviations and acronyms border color
+$abbr-border-color:           $gray-light !default;
+//** Headings small color
+$headings-small-color:        $gray-light !default;
+//** Blockquote small color
+$blockquote-small-color:      $gray-light !default;
+//** Blockquote font size
+$blockquote-font-size:        ($font-size-base * 1.25) !default;
+//** Blockquote border color
+$blockquote-border-color:     $gray-lighter !default;
+//** Page header border color
+$page-header-border-color:    $gray-lighter !default;
+//** Width of horizontal description list titles
+$dl-horizontal-offset:        $component-offset-horizontal !default;
+//** Point at which .dl-horizontal becomes horizontal
+$dl-horizontal-breakpoint:    $grid-float-breakpoint !default;
+//** Horizontal line color.
+$hr-border:                   $gray-lighter !default;
diff --git a/lab-raziyeh/app/scss/lib/_custom-bootstrap.scss b/lab-raziyeh/app/scss/lib/_custom-bootstrap.scss
new file mode 100644
index 0000000..74cd7a9
--- /dev/null
+++ b/lab-raziyeh/app/scss/lib/_custom-bootstrap.scss
@@ -0,0 +1,57 @@
+/*!
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
+ * Copyright 2011-2016 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+// Core variables and mixins
+//@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
+@import "custom-bootstrap-variables";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/mixins";
+
+// Reset and dependencies
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/normalize";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/print";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/glyphicons";
+
+// Core CSS
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/scaffolding";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/type";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/code";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/grid";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/tables";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/forms";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/buttons";
+
+// Components
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/component-animations";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/dropdowns";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/button-groups";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/input-groups";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/navs";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/navbar";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/breadcrumbs";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/pagination";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/pager";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/labels";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/badges";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/jumbotron";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/thumbnails";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/alerts";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/progress-bars";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/media";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/list-group";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/panels";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-embed";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/wells";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/close";
+
+// Components w/ JavaScript
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/modals";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/tooltip";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/popovers";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/carousel";
+
+// Utility classes
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/utilities";
+@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities";
diff --git a/lab-raziyeh/app/scss/lib/_theme.scss b/lab-raziyeh/app/scss/lib/_theme.scss
new file mode 100644
index 0000000..6b13123
--- /dev/null
+++ b/lab-raziyeh/app/scss/lib/_theme.scss
@@ -0,0 +1,14 @@
+$app-primary: #a7f7fa;
+$app-secondary: #7fa7af;
+$app-disabled: #abeaeb;
+$app-active: #fa7fa7;
+$app-success: #a7ff7a;
+$app-error: #ff7a7a;
+$app-warn: #ffa77a;
+$app-info: #7aa7ff;
+$app-white: #fff;
+$app-black: #000;
+
+.navbar-nav{
+  margin-top:5px;
+}
\ No newline at end of file
diff --git a/lab-raziyeh/app/scss/lib/_vendor.scss b/lab-raziyeh/app/scss/lib/_vendor.scss
new file mode 100644
index 0000000..693ed46
--- /dev/null
+++ b/lab-raziyeh/app/scss/lib/_vendor.scss
@@ -0,0 +1,4 @@
+
+@import "custom-bootstrap";
+// font-awesome
+@import "~font-awesome/scss/font-awesome.scss";
\ No newline at end of file
diff --git a/lab-raziyeh/app/scss/main.scss b/lab-raziyeh/app/scss/main.scss
new file mode 100644
index 0000000..e91e094
--- /dev/null
+++ b/lab-raziyeh/app/scss/main.scss
@@ -0,0 +1,9 @@
+@import "_custom-bootstrap";
+@import "theme";
+
+html, body {
+  width: 100%; 
+  height: 100%; 
+  background:#f5f5f5;
+}
+
diff --git a/lab-raziyeh/app/service/auth-service.js b/lab-raziyeh/app/service/auth-service.js
new file mode 100644
index 0000000..a9c4656
--- /dev/null
+++ b/lab-raziyeh/app/service/auth-service.js
@@ -0,0 +1,85 @@
+'use strict';
+
+module.exports = ['$q', '$log', '$http', '$window', authService];
+
+function authService($q, $log, $http, $window){
+  $log.debug('init authService');
+  // create service
+  let service = {};
+  service.token = null;
+
+  service.setToken = function(_token){
+    $log.debug('authService.service.setToken()');
+    if (! _token)
+      return $q.reject(new Error('no service.token'));
+    $window.localStorage.setItem('token', _token);
+    service.token = _token;
+    return $q.resolve(service.token);
+  };
+
+  service.getToken = function(){
+    $log.debug('authService.getToken');
+    if (service.token) return $q.resolve(service.token);
+    service.token = $window.localStorage.getItem('service.token');
+    if (service.token) return $q.resolve(service.token);
+    return $q.reject(new Error('token not found'));
+  };
+  
+  service.logout = function(){
+    $log.debug('authService.logout()');
+    $window.localStorage.removeItem('service.token');
+    service.token = null;
+    return $q.resolve();
+  };
+
+  service.signup = function(user) {
+    $log.debug('authService.signup()');
+    let url = `${__API_URL__}/api/signup`;
+    console.log('signup url', url);
+
+    let config = {
+      headers: {
+        'Content-Type': 'application/json',
+        'Accept': 'application/json',
+      },
+    };
+
+    return $http.post(url, user, config)
+    .then( res => {
+      $log.log('success', res.data);
+      // res.data is the response body aka the service.token 
+      return service.setToken(res.data);
+    })
+    .catch(err => {
+      $log.error('fail', err.message);
+      return $q.reject(err);
+    });
+  };
+
+  service.login = function(user){
+    $log.debug('authService.login()');
+    let url = `${__API_URL__}/api/login`;
+    // base64 encoded 'username:password'
+    let base64 = $window.btoa(`${user.username}:${user.password}`);
+
+    let config = {
+      headers: {
+        Accept: 'application/json',
+        Authorization: `Basic ${base64}`,
+      },
+    };
+
+    return $http.get(url, config)
+    .then( res => {
+      $log.log('success', res.data);
+      return service.setToken(res.data);
+    })
+    .catch( err => {
+      $log.error(err.message);
+      return $q.reject(err);
+    });
+  };
+
+  // return service
+  return service;
+}
\ No newline at end of file
diff --git a/lab-raziyeh/app/service/gallery-service.js b/lab-raziyeh/app/service/gallery-service.js
new file mode 100644
index 0000000..dbef112
--- /dev/null
+++ b/lab-raziyeh/app/service/gallery-service.js
@@ -0,0 +1,122 @@
+'use strict';
+
+module.exports = ['$q', '$log', '$http', 'authService', function($q, $log, $http , authService) {
+  $log.debug('init gallery Service');
+
+  let service = {};
+
+  service.galleries = [];
+  service.createGallery = function(gallery) {
+    $log.debug('galleryService.createGallery()');
+
+    return authService.getToken() 
+    .then( token => {
+      let url = `${__API_URL__}/api/gallery`;
+      let config = {
+        headers: {
+          Accept: 'application/json',
+          'Content-Type': 'application/json',
+          Authorization: `Bearer ${token}`,
+        },
+      };
+
+      return $http.post(url, gallery, config);
+    })
+    .then(res => {
+      $log.log('successful create gallery');
+      let gallery = res.data;
+      service.galleries.unshift(gallery);
+      return gallery;
+    })
+    .catch( err => {
+      $log.error(err.message);
+      return $q.reject(err);
+    });
+  };
+
+  service.deleteGallery = function (galleryID){ 
+    $log.debug('galleryService.deleteGallery()');
+    return authService.getToken()
+    .then( token => {
+      let url = `${__API_URL__}/api/gallery/${galleryID}`;
+      let config = {
+        headers: {
+          Authorization: `Bearer ${token}`,
+          Accept: 'application/json, text/plain, */*',
+        },
+      };
+
+      return $http.delete(url, config);
+    })
+    .then( () => {
+      $log.log('successful delete user galleries');
+      for (let i=0; i< service.galleries.length; i++) {
+        if(service.galleries[i]._id === galleryID)
+          service.galleries.splice(i,1);
+      }
+      return;
+    })
+    .catch(err => {
+      return $q.reject(err);
+    });
+  };
+
+  service.fetchGalleries = function() {
+    $log.debug('galleryService.fetchGallery()');
+
+    return authService.getToken()
+    .then( token => {
+      let url = `${__API_URL__}/api/gallery/`;
+      let config = {
+        headers: {
+          Accept: 'application/json',
+          Authorization: `Bearer ${token}`,
+        },
+      };
+
+      return $http.get(url, config);
+    })
+    .then(res => {
+      $log.log('successful fetch user galleries');
+      service.galleries = res.data;
+      return service.galleries;
+    })
+    .catch(err => {
+      $log.error(err.message);
+      return $q.reject(err);
+    });
+  };
+
+  service.updateGallery = function (galleryID, galleryData){ 
+    $log.debug('galleryService.updateGallery()');
+    return authService.getToken()
+    .then( token => {
+      let url = `${__API_URL__}/api/gallery/${galleryID}`;
+      let config = {
+        headers: {
+          Accept: 'application/json',
+          'Content-Type': 'application/json',
+          Authorization: `Bearer ${token}`,
+        },
+      };
+
+      return $http.put(url, galleryData, config);
+    })
+    .then( res => {
+      let gallery = res.data;
+      $log.log('successful update user galleries');
+      for( let i=0; i< service.galleries.length; i++) {
+        if(galleryID === service.galleries[i]._id) {
+          service.galleries[i] = gallery;
+        }
+      }
+      return gallery;
+    })
+    .catch(err => {
+      $log.error(err.message);
+      return $q.reject(err);
+    });
+  };
+
+  return service;
+}];
\ No newline at end of file
diff --git a/lab-raziyeh/app/service/pic-service.js b/lab-raziyeh/app/service/pic-service.js
new file mode 100644
index 0000000..4c68ca3
--- /dev/null
+++ b/lab-raziyeh/app/service/pic-service.js
@@ -0,0 +1,69 @@
+'use strict';
+
+module.exports = ['$q', '$log', '$http', 'Upload', 'authService', picService];
+
+function picService($q, $log, $http, Upload, authService){
+  $log.debug('init picService');
+  let service = {};
+
+  service.uploadGalleryPic = function(galleryData, picData){
+    $log.debug('picService.uploadGalleryPic()');
+
+    return authService.getToken()
+    .then( token => {
+      let url = `${__API_URL__}/api/gallery/${galleryData._id}/pic`;
+      let headers = {
+        Authorization: `Bearer ${token}`,
+        Accept: 'application/json',
+      };
+
+      return Upload.upload({
+        url,
+        headers,
+        method: 'POST',
+        data: {
+          name: picData.name,
+          desc: picData.desc,
+          file: picData.file,
+        },
+      });
+    })
+    .then(res => {
+      galleryData.pics.unshift(res.data);
+      $log.log('success\n', res.data);
+      return res.data;
+    })
+    .catch( err => {
+      $log.error(err.message);
+      return $q.reject(err);
+    });
+  };
+
+
+  service.deleteGalleryPic = function(galleryData, picData){
+    $log.debug('picService.deleteGalleryPic()');
+    return authService.getToken()
+    .then( token => {
+      let url = `${__API_URL__}/api/gallery/${galleryData._id}/pic/${picData._id}`;
+      let config = {
+        headers: {
+          Accept: 'application/json',
+          Authorization: `Bearer ${token}`,
+        },
+      };
+
+      return $http.delete(url, config);
+    })
+    .then( () => {
+      $log.log('successful delete Pic');
+      var index = galleryData.pics.indexOf(picData._id);
+      return galleryData.pics.splice(index,1);
+    })
+    .catch(err => {
+      $log.error(err.message);
+      return $q.reject(err);
+    });
+  };
+
+  return service;
+}
\ No newline at end of file
diff --git a/lab-raziyeh/app/view/home/_home.scss b/lab-raziyeh/app/view/home/_home.scss
new file mode 100644
index 0000000..872b4fc
--- /dev/null
+++ b/lab-raziyeh/app/view/home/_home.scss
@@ -0,0 +1,19 @@
+@import "theme";
+
+.home {
+  .panel-heading {
+    background-color: $app-primary;
+    span {
+      font-weight: bold;
+      color: $app-secondary;
+    }
+
+    span:hover {
+      color: $app-black;
+    }
+  }
+}
+
+.textBold {
+  font-weight: 600;
+}
\ No newline at end of file
diff --git a/lab-raziyeh/app/view/home/home-controller.js b/lab-raziyeh/app/view/home/home-controller.js
new file mode 100644
index 0000000..bea131a
--- /dev/null
+++ b/lab-raziyeh/app/view/home/home-controller.js
@@ -0,0 +1,34 @@
+'use strict';
+
+require('./_home.scss');
+
+module.exports = ['$log', '$rootScope', 'galleryService', HomeController ];
+
+function HomeController($log, $rootScope, galleryService){
+  $log.debug('init homeCtrl');
+  this.galleries = [];
+
+  this.fetchGalleries = function(){
+    galleryService.fetchGalleries()
+    .then( galleries => {
+      this.galleries = galleries;
+      this.currentGallery = galleries[0];
+      console.log('this.galleries', this.galleries);
+    });
+  };
+
+  this.galleryDeleteDone = function(gallery){
+    $log.debug('homeCtrl.galleryDeleteDone()');
+    if (this.currentGallery._id == gallery._id){
+      this.currentGallery = null;
+    }
+  };
+
+  this.fetchGalleries();
+
+  // when the locationChangeSuccess fetchGalleries
+  $rootScope.$on('$locationChangeSuccess', () => {
+    this.fetchGalleries();
+  });
+
+}
\ No newline at end of file
diff --git a/lab-raziyeh/app/view/home/home.html b/lab-raziyeh/app/view/home/home.html
new file mode 100644
index 0000000..515dab7
--- /dev/null
+++ b/lab-raziyeh/app/view/home/home.html
@@ -0,0 +1,17 @@
+
+
+ + +
+ + + + + + +
+
\ No newline at end of file diff --git a/lab-raziyeh/app/view/landing/_landing.scss b/lab-raziyeh/app/view/landing/_landing.scss new file mode 100644 index 0000000..9c9f32f --- /dev/null +++ b/lab-raziyeh/app/view/landing/_landing.scss @@ -0,0 +1,30 @@ +@import "_theme"; + +.landing { + background-color: $app-secondary; +} + +.navbar-inverse { + background-color: #6f94a2 !important; + border-color: #6f94a2 !important; +} +.navbar-inverse a { + color:#fff!important; + font-weight: 400; +} +.navbar-nav > li > a { + padding-top:10px !important; + padding-bottom:10px !important; +} +img { + max-width: 100px; +} + +// .mainDiv{ +// width: 100%; +// height: 100%; +// background-image: url('../data/background.png'); +// background-repeat: no-repeat; +// background-size: cover; +// margin: 0 auto; +// } \ No newline at end of file diff --git a/lab-raziyeh/app/view/landing/landing-controller.js b/lab-raziyeh/app/view/landing/landing-controller.js new file mode 100644 index 0000000..1736688 --- /dev/null +++ b/lab-raziyeh/app/view/landing/landing-controller.js @@ -0,0 +1,13 @@ +'use strict'; + +require('./_landing.scss'); + +module.exports = ['$log', function LandingController($log){ + $log.debug('Landing controller'); + + this.showsignin = false; + + this.signin = function() { + this.showsignin? this.showsignin = false : this.showsignin = true; + }; +}]; diff --git a/lab-raziyeh/app/view/landing/landing.html b/lab-raziyeh/app/view/landing/landing.html new file mode 100644 index 0000000..80e333c --- /dev/null +++ b/lab-raziyeh/app/view/landing/landing.html @@ -0,0 +1,3 @@ + + +
\ No newline at end of file diff --git a/lab-raziyeh/app/view/login/_login.scss b/lab-raziyeh/app/view/login/_login.scss new file mode 100644 index 0000000..091f421 --- /dev/null +++ b/lab-raziyeh/app/view/login/_login.scss @@ -0,0 +1,8 @@ +div.login { + margin-top: 100px; +} + +.active { + color: #111; + font-weight: 600; +} \ No newline at end of file diff --git a/lab-raziyeh/app/view/login/login-controller.js b/lab-raziyeh/app/view/login/login-controller.js new file mode 100644 index 0000000..e49dbe3 --- /dev/null +++ b/lab-raziyeh/app/view/login/login-controller.js @@ -0,0 +1,14 @@ +'use strict'; + +require('./_login.scss'); + +module.exports = ['$log', function LoginController($log){ + $log.debug('Login controller'); + + this.showsignin = false; + + this.signin = function() { + this.showsignin? this.showsignin = false : this.showsignin = true; + }; + +}]; \ No newline at end of file diff --git a/lab-raziyeh/app/view/login/login.html b/lab-raziyeh/app/view/login/login.html new file mode 100644 index 0000000..f9febc6 --- /dev/null +++ b/lab-raziyeh/app/view/login/login.html @@ -0,0 +1,30 @@ + + \ No newline at end of file diff --git a/lab-raziyeh/client-test/controller-test/create-gallery-controller-test.js b/lab-raziyeh/client-test/controller-test/create-gallery-controller-test.js new file mode 100644 index 0000000..c7dce3d --- /dev/null +++ b/lab-raziyeh/client-test/controller-test/create-gallery-controller-test.js @@ -0,0 +1,50 @@ +'use strict'; + +describe('testing edit-gallery controller', function() { + var url = 'http://localhost:3000/api/gallery'; + + beforeEach(() => { + angular.mock.module('demoApp'); + angular.mock.inject(($rootScope, authService, $componentController, $httpBackend ) => { + authService.setToken('1234'); + + this.authService = authService; + this.$rootScope = $rootScope; + this.$componentController = $componentController; + this.$httpBackend = $httpBackend; + }); + }); + + afterEach(() => { + this.$httpBackend.verifyNoOutstandingExpectation(); + this.$httpBackend.verifyNoOutstandingRequest(); + this.authService.logout(); + }); + + describe('testing createGalley', () => { + it('it should create a gallery', () => { + let gallery = { + name: 'lulwat', + desc: 'hello', + }; + let headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + Authorization: 'Bearer 1234', + }; + + this.$httpBackend.expectPOST(url, gallery, headers) + .respond(200, {_id:'23770504', username: 'Rozi', name: 'xxx', desc:'xxx' , pics: []}); + + let createGalleryCtrl = this.$componentController('createGallery'); + + createGalleryCtrl.gallery = { name: 'lulwat', desc: 'hello'}; + createGalleryCtrl.createGallery(); + this.$httpBackend.flush(); + this.$rootScope.$apply(); + + expect(createGalleryCtrl.gallery.name).toBe.null; + expect(createGalleryCtrl.gallery.desc).toBe.null; + }); + }); +}); \ No newline at end of file diff --git a/lab-raziyeh/client-test/controller-test/edit-gallery-controller-test.js b/lab-raziyeh/client-test/controller-test/edit-gallery-controller-test.js new file mode 100644 index 0000000..7db1bdc --- /dev/null +++ b/lab-raziyeh/client-test/controller-test/edit-gallery-controller-test.js @@ -0,0 +1,67 @@ +'use strict'; + +describe('testing edit-gallery controller', function() { + var url = 'http://localhost:3000/api/gallery'; + + beforeEach(() => { + angular.mock.module('demoApp'); + angular.mock.inject(($rootScope, authService, $componentController, $httpBackend ) => { + authService.setToken('1234'); + + this.authService = authService; + this.$rootScope = $rootScope; + this.$componentController = $componentController; + this.$httpBackend = $httpBackend; + }); + }); + + afterEach(() => { + this.$httpBackend.verifyNoOutstandingExpectation(); + this.$httpBackend.verifyNoOutstandingRequest(); + this.authService.logout(); + }); + + describe('testing component', () => { + it('testing component bindings', () => { + let mockBindings = { + gallery: { + name: 'lulwat', + desc: 'this is a test', + }, + }; + + let editGalleryCtrl = this.$componentController('editGallery', null, mockBindings); + expect(editGalleryCtrl.gallery.name).toEqual(mockBindings.gallery.name); + expect(editGalleryCtrl.gallery.desc).toEqual(mockBindings.gallery.desc); + this.$rootScope.$apply(); + }); + }); + + describe('testing updateGalley', () => { + it('it make a valid PUT request', () => { + let headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + Authorization: 'Bearer 1234', + }; + + this.$httpBackend.expectPUT(`${url}/12345`, {_id: '12345', name: 'new name', desc: 'hello'}, headers) + .respond(200); + + let mockBindings = { + gallery: { + _id: '12345', + name: 'lulwat', + desc: 'hello', + }, + }; + let editGalleryCtrl = this.$componentController('editGallery', null, mockBindings); + editGalleryCtrl.gallery.name = 'new name'; + + editGalleryCtrl.updateGallery(); + this.$httpBackend.flush(); + this.$rootScope.$apply(); + }); + }); + +}); \ No newline at end of file diff --git a/lab-raziyeh/client-test/controller-test/gallery-items-controller-test.js b/lab-raziyeh/client-test/controller-test/gallery-items-controller-test.js new file mode 100644 index 0000000..f2cecf3 --- /dev/null +++ b/lab-raziyeh/client-test/controller-test/gallery-items-controller-test.js @@ -0,0 +1,70 @@ +'use strict'; + +describe('testing gallery-li controller', function(){ + beforeEach(() => { + angular.mock.module('demoApp'); + angular.mock.inject(($rootScope, authService, $componentController, $httpBackend) => { + authService.setToken('secrettoken'); + + this.authService = authService; + this.$httpBackend = $httpBackend; + this.$rootScope = $rootScope; + this.$componentController = $componentController; + }); + }); + + afterEach(() => { + this.$httpBackend.verifyNoOutstandingExpectation(); + this.$httpBackend.verifyNoOutstandingRequest(); + this.authService.logout(); + }); + + describe('testing #deleteDone', () => { + it('should call deleteDone', () => { + let mockBindings = { + gallery: { + _id: '65432ONE', + name: 'hello', + desc: 'infomative', + pics: [], + }, + deleteDone: function(data){ + expect(data.galleryData._id).toEqual('65432ONE'); + }, + }; + + let galleryLICtrl = this.$componentController('galleryItems', null, mockBindings); + galleryLICtrl.deleteDone({galleryData: galleryLICtrl.gallery}); + + this.$rootScope.$apply(); + }); + }); + + it('should call deleteDone with gallery after galleryDelete', () => { + let url = 'http://localhost:3000/api/gallery/65432ONE'; + let headers = { + Authorization: 'Bearer secrettoken', + Accept: 'application/json, text/plain, */*', + }; + + let mockBindings = { + gallery: { + _id: '65432ONE', + name: 'hello', + desc: 'infomative', + pics: [], + }, + deleteDone: function(data){ + expect(data.galleryData._id).toEqual(mockBindings.gallery._id); + }, + }; + + this.$httpBackend.expectDELETE(url, headers).respond(204); + + let galleryLICtrl = this.$componentController('galleryItems', null, mockBindings); + galleryLICtrl.deleteGallery(); + + this.$httpBackend.flush(); + this.$rootScope.$apply(); + }); +}); \ No newline at end of file diff --git a/lab-raziyeh/client-test/controller-test/signin-controller-test.js b/lab-raziyeh/client-test/controller-test/signin-controller-test.js new file mode 100644 index 0000000..9242941 --- /dev/null +++ b/lab-raziyeh/client-test/controller-test/signin-controller-test.js @@ -0,0 +1,54 @@ +'use strict'; + +describe('testing edit-gallery controller', function() { + var url = 'http://localhost:3000/api/login'; + + beforeEach(() => { + angular.mock.module('demoApp'); + angular.mock.inject(($rootScope, authService, $componentController, $httpBackend, $location, $window) => { + authService.setToken('1234'); + + this.authService = authService; + this.$rootScope = $rootScope; + this.$componentController = $componentController; + this.$httpBackend = $httpBackend; + this.$location = $location; + this.$window = $window; + }); + }); + + afterEach(() => { + this.$httpBackend.verifyNoOutstandingExpectation(); + this.$httpBackend.verifyNoOutstandingRequest(); + this.authService.logout(); + }); + + describe('testing signin', () => { + it('it should create a user', () => { + + let user = { + name: 'lulwat', + password: '1234', + username: 'rozi', + }; + + var base64 = this.$window.btoa(`${user.username}:${user.password}`); + + let headers = { + 'Accept': 'application/json', + Authorization:`Basic ${base64}`, + }; + + this.$httpBackend.expectGET(url, headers) + .respond(200, {_id:'23770504', username: 'Rozi', name: 'lulwat', password:'1234'}); + + let signinCtrl = this.$componentController('signin'); + + signinCtrl.signin(user); + this.$httpBackend.flush(); + this.$rootScope.$apply(); + + expect(this.$location.path()).toBe('/home'); + }); + }); +}); \ No newline at end of file diff --git a/lab-raziyeh/client-test/controller-test/signup-controller-test.js b/lab-raziyeh/client-test/controller-test/signup-controller-test.js new file mode 100644 index 0000000..c566cd4 --- /dev/null +++ b/lab-raziyeh/client-test/controller-test/signup-controller-test.js @@ -0,0 +1,50 @@ +'use strict'; + +describe('testing edit-gallery controller', function() { + var url = 'http://localhost:3000/api/signup'; + + beforeEach(() => { + angular.mock.module('demoApp'); + angular.mock.inject(($rootScope, authService, $componentController, $httpBackend, $location) => { + authService.setToken('1234'); + + this.authService = authService; + this.$rootScope = $rootScope; + this.$componentController = $componentController; + this.$httpBackend = $httpBackend; + this.$location = $location; + }); + }); + + afterEach(() => { + this.$httpBackend.verifyNoOutstandingExpectation(); + this.$httpBackend.verifyNoOutstandingRequest(); + this.authService.logout(); + }); + + describe('testing signup', () => { + it('it should create a user', () => { + let user = { + name: 'lulwat', + password: '1234', + username: 'rozi', + }; + + let headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }; + + this.$httpBackend.expectPOST(url, user, headers) + .respond(200, {_id:'23770504', username: 'Rozi', name: 'lulwat', password:'1234'}); + + let signupCtrl = this.$componentController('signup'); + + signupCtrl.signup(user); + this.$httpBackend.flush(); + this.$rootScope.$apply(); + + expect(this.$location.path()).toBe('/home'); + }); + }); +}); \ No newline at end of file diff --git a/lab-raziyeh/client-test/controller-test/thumbnail-container-controller-test.js b/lab-raziyeh/client-test/controller-test/thumbnail-container-controller-test.js new file mode 100644 index 0000000..c58a167 --- /dev/null +++ b/lab-raziyeh/client-test/controller-test/thumbnail-container-controller-test.js @@ -0,0 +1,39 @@ +'use strict'; + +describe('testing edit-gallery controller', function() { + var url = 'http://localhost:3000/api/gallery'; + + beforeEach(() => { + angular.mock.module('demoApp'); + angular.mock.inject(($rootScope, authService, $componentController, $httpBackend ) => { + authService.setToken('1234'); + + this.authService = authService; + this.$rootScope = $rootScope; + this.$componentController = $componentController; + this.$httpBackend = $httpBackend; + }); + }); + + afterEach(() => { + this.$httpBackend.verifyNoOutstandingExpectation(); + this.$httpBackend.verifyNoOutstandingRequest(); + this.authService.logout(); + }); + + describe('testing component', () => { + it('testing component bindings', () => { + let mockBindings = { + gallery: { + name: 'lulwat', + desc: 'this is a test', + }, + }; + + let thumbnailContainerCtrl = this.$componentController('thumbnailContainer', null, mockBindings); + expect(thumbnailContainerCtrl.gallery.name).toEqual(mockBindings.gallery.name); + expect(thumbnailContainerCtrl.gallery.desc).toEqual(mockBindings.gallery.desc); + this.$rootScope.$apply(); + }); + }); +}); \ No newline at end of file diff --git a/lab-raziyeh/client-test/service-test/auth-service-test.js b/lab-raziyeh/client-test/service-test/auth-service-test.js new file mode 100644 index 0000000..8f609d7 --- /dev/null +++ b/lab-raziyeh/client-test/service-test/auth-service-test.js @@ -0,0 +1,86 @@ +'use strict'; + +describe('testing auth service', function() { + beforeEach(() => { + angular.mock.module('demoApp'); + angular.mock.inject(($rootScope, authService, $window, $httpBackend) => { + this.authService = authService; + this.$window = $window; + this.$rootScope = $rootScope; + authService.setToken('1234'); + + this.$httpBackend = $httpBackend; + }); + }); + + var url = 'http://localhost:3000/api'; + var user = { + name: 'Rozi', + username: 'rozibzargan', + password: '123', + }; + + describe('testing setToken()', ()=> { + it('should return a Token', ()=> { + this.authService.setToken('hello') + .then(token => { + expect(token).toBe('hello'); + }); + + this.$rootScope.$apply(); + }); + }); + + describe('testing getToken()', ()=> { + it('should return a Token', ()=> { + this.authService.token = null; + this.$window.localStorage.setItem('token', 'hello'); + + this.authService.getToken() + .then(token => { + expect(token).toBe('hello'); + }); + + this.$rootScope.$apply(); + }); + }); + + describe('testing sign up', () => { + it('should create user', () => { + + let headers = { + 'Content-Type': 'application/json', + Accept: 'application/json', + }; + + this.$httpBackend.expectPOST(`${url}/signup`, user, headers) + .respond(200, '100001'); + + this.authService.signup(user) + .then(token => { + expect(token).toBe('100001'); + }); + this.$httpBackend.flush(); + }); + }); + + describe('testing sign in', () => { + it('should return a user', () => { + var base64 = this.$window.btoa(`${user.username}:${user.password}`); + + let headers = { + Accept: 'application/json', + Authorization: `Basic ${base64}`, + }; + + this.$httpBackend.expectGET(`${url}/login`, headers) + .respond(200, '10000'); + + this.authService.login(user) + .then(token => { + expect(token).toBe('10000'); + }); + this.$httpBackend.flush(); + }); + }); +}); \ No newline at end of file diff --git a/lab-raziyeh/client-test/service-test/gallery-service-test.js b/lab-raziyeh/client-test/service-test/gallery-service-test.js new file mode 100644 index 0000000..016373b --- /dev/null +++ b/lab-raziyeh/client-test/service-test/gallery-service-test.js @@ -0,0 +1,125 @@ +'use strict'; + +describe('testing gallery service', function() { + var url = 'http://localhost:3000/api'; + let galleryData = { + name: 'documents', + desc: 'all notes from my interviews', + }; + + beforeEach(() => { + angular.mock.module('demoApp'); + angular.mock.inject((authService, galleryService, $httpBackend, $window) => { + this.authService = authService; + authService.setToken('1234'); + + this.galleryService = galleryService; + this.$httpBackend = $httpBackend; + this.$window = $window; + }); + }); + + afterEach(() => { + this.authService.setToken(null); + this.$window.localStorage.clear(); + }); + + describe('testing galleryService.createGallery', () => { + it('should return a gallery', () => { + let galleryData = { + name: 'documents', + desc: 'all notes from my interviews', + }; + + let headers = { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: 'Bearer 1234', + }; + + this.$httpBackend.expectPOST(`${url}/gallery`, galleryData, headers) + .respond(200, {_id:'23770504', username: 'Rozi', name: galleryData.name, desc: galleryData.desc, pics: []}); + + this.galleryService.createGallery(galleryData) + .then(gallery => { + expect(gallery._id).toBe('23770504'); + expect(gallery.name).toBe(galleryData.name); + expect(gallery.desc).toBe(galleryData.desc); + }); + this.$httpBackend.flush(); + }); + }); + + describe('testing galleryService.fetchGalleries', () => { + let galleries = [ + { + name: 'documents', + desc: 'all notes from my interviews', + }, + { + name: 'documents2', + desc: 'all notes from my interviews', + }, + { + name: 'documents3', + desc: 'all notes from my interviews', + }, + + ]; + + it('should return a gallery', () => { + + let headers = { + Accept: 'application/json', + Authorization: 'Bearer 1234', + }; + + this.$httpBackend.expectGET(`${url}/gallery/`, headers) + .respond(200, galleries); + + this.galleryService.fetchGalleries() + .then(galleries => { + expect(galleries.length).toBe(3); + expect(galleries[0].name).toBe(galleries[0].name); + expect(galleries[0].desc).toBe(galleries[0].desc); + }); + this.$httpBackend.flush(); + }); + }); + + describe('testing galleryService.updateGallery', () => { + it('should update a gallery', () => { + let galleryId = '1000', + headers = { + 'Content-Type':'application/json', + Accept: 'application/json', + Authorization:'Bearer 1234', + }; + this.$httpBackend.expectPUT(`${url}/gallery/1000`, galleryData, headers) + .respond(200, {_id:'1000', username: 'Rozi', name: 'updated name', desc: 'updated Desc', pics: []}); + + this.galleryService.updateGallery(galleryId, galleryData) + .then(gallery => { + expect(gallery._id).toBe(galleryId); + expect(gallery.name).toEqual('updated name'); + expect(gallery.desc).toEqual('updated Desc'); + }); + this.$httpBackend.flush(); + }); + }); + + describe('testing galleryService.deleteGallery', () => { + it('should delete a gallery', () => { + let galleryId = '1000', + headers = { + Authorization: 'Bearer 1234', + Accept: 'application/json, text/plain, */*', + }; + this.$httpBackend.expectDELETE(`${url}/gallery/1000`, headers) + .respond(204); + + this.galleryService.deleteGallery(galleryId); + this.$httpBackend.flush(); + }); + }); +}); \ No newline at end of file diff --git a/lab-raziyeh/client-test/service-test/pic-service-test.js b/lab-raziyeh/client-test/service-test/pic-service-test.js new file mode 100644 index 0000000..c38148e --- /dev/null +++ b/lab-raziyeh/client-test/service-test/pic-service-test.js @@ -0,0 +1,66 @@ +// 'use strict'; + +// describe('testing Pic service', function() { +// var url = 'http://localhost:3000/api'; +// // let galleryData = { +// // name: 'documents', +// // desc: 'all notes from my interviews', +// // }; + + +// beforeEach(() => { +// angular.mock.module('demoApp'); +// angular.mock.inject(($rootScope,authService, galleryService, picService, $httpBackend, Upload) => { +// this.authService = authService; +// authService.setToken('1234'); +// this.$rootScope =$rootScope ; +// this.galleryService = galleryService; +// this.picService = picService; +// this.$httpBackend = $httpBackend; +// this.$upload = Upload; +// }); +// }); + +// describe('testing picService.uploadGalleryPic', () => { +// let gallery = { +// _id: '111', +// name: 'documents', +// desc: 'all notes from my interviews', +// }; + +// it('should create a pic', () => { + + +// var pic = { +// name: 'test', +// desc: 'test data', +// file: `${__dirname}/data/razi.jpg`, +// }; + +// let headers = { +// 'Content-Type': 'application/json', +// Accept: 'application/json', +// Authorization: 'Bearer 1234', +// }; + +// this.$httpBackend.expectPOST(`${url}/gallery/${111}/pic`, pic, headers) +// .respond(200); +// // { +// // name: pic.name, +// // desc: pic.desc, +// // imageURI: 'https://gallery01.s3.amazonaws.com/e44df56e5139f271c341736e82065aae.png', +// // objectKey: 'e44df56e5139f271c341736e82065aae.png', +// // userID: '"58111458118eb6427278f8b0"', +// // username:'omidomid'} + +// this.picService.uploadGalleryPic(gallery, pic) +// .then((data) => { +// expect(data.name).toBe(pic.name); + +// }); +// this.$httpBackend.flush(); +// this.$rootScope.$apply(); + +// }); +// }); +// }); \ No newline at end of file diff --git a/lab-raziyeh/db/WiredTiger b/lab-raziyeh/db/WiredTiger new file mode 100644 index 0000000..f586677 --- /dev/null +++ b/lab-raziyeh/db/WiredTiger @@ -0,0 +1,2 @@ +WiredTiger +WiredTiger 2.8.1: (March 24, 2016) diff --git a/lab-raziyeh/db/WiredTiger.lock b/lab-raziyeh/db/WiredTiger.lock new file mode 100644 index 0000000..3d84206 --- /dev/null +++ b/lab-raziyeh/db/WiredTiger.lock @@ -0,0 +1 @@ +WiredTiger lock file diff --git a/lab-raziyeh/db/WiredTiger.turtle b/lab-raziyeh/db/WiredTiger.turtle new file mode 100644 index 0000000..d257c74 --- /dev/null +++ b/lab-raziyeh/db/WiredTiger.turtle @@ -0,0 +1,6 @@ +WiredTiger version string +WiredTiger 2.8.1: (March 24, 2016) +WiredTiger version +major=2,minor=8,patch=1 +file:WiredTiger.wt +allocation_size=4KB,app_metadata=,block_allocation=best,block_compressor=,cache_resident=0,checkpoint=(WiredTigerCheckpoint.279=(addr="018981e4449adf688a81e4b4b8def28b81e42f5baaf1808080e3016fc0e311dfc0",order=279,time=1478558181,size=1179648,write_gen=685)),checkpoint_lsn=(8,3584),checksum=uncompressed,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,internal_item_max=0,internal_key_max=0,internal_key_truncate=,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=),memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=0,prefix_compression_min=4,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=75,value_format=S,version=(major=1,minor=1) diff --git a/lab-raziyeh/db/WiredTiger.wt b/lab-raziyeh/db/WiredTiger.wt new file mode 100644 index 0000000..445bec0 Binary files /dev/null and b/lab-raziyeh/db/WiredTiger.wt differ diff --git a/lab-raziyeh/db/WiredTigerLAS.wt b/lab-raziyeh/db/WiredTigerLAS.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/WiredTigerLAS.wt differ diff --git a/lab-raziyeh/db/_mdb_catalog.wt b/lab-raziyeh/db/_mdb_catalog.wt new file mode 100644 index 0000000..fae007b Binary files /dev/null and b/lab-raziyeh/db/_mdb_catalog.wt differ diff --git a/lab-raziyeh/db/collection-0--3143307283938210215.wt b/lab-raziyeh/db/collection-0--3143307283938210215.wt new file mode 100644 index 0000000..2c6ffe1 Binary files /dev/null and b/lab-raziyeh/db/collection-0--3143307283938210215.wt differ diff --git a/lab-raziyeh/db/collection-0-6564260200608951873.wt b/lab-raziyeh/db/collection-0-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/collection-0-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/collection-0-8845950676657731124.wt b/lab-raziyeh/db/collection-0-8845950676657731124.wt new file mode 100644 index 0000000..03beea9 Binary files /dev/null and b/lab-raziyeh/db/collection-0-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/collection-3-6564260200608951873.wt b/lab-raziyeh/db/collection-3-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/collection-3-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/collection-3-8845950676657731124.wt b/lab-raziyeh/db/collection-3-8845950676657731124.wt new file mode 100644 index 0000000..49879e5 Binary files /dev/null and b/lab-raziyeh/db/collection-3-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/collection-9-6564260200608951873.wt b/lab-raziyeh/db/collection-9-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/collection-9-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/collection-9-8845950676657731124.wt b/lab-raziyeh/db/collection-9-8845950676657731124.wt new file mode 100644 index 0000000..ccc0d7c Binary files /dev/null and b/lab-raziyeh/db/collection-9-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/diagnostic.data/metrics.2016-10-25T17-11-24Z-00000 b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-25T17-11-24Z-00000 new file mode 100644 index 0000000..378c6e6 Binary files /dev/null and b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-25T17-11-24Z-00000 differ diff --git a/lab-raziyeh/db/diagnostic.data/metrics.2016-10-25T20-27-51Z-00000 b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-25T20-27-51Z-00000 new file mode 100644 index 0000000..df7b8f7 Binary files /dev/null and b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-25T20-27-51Z-00000 differ diff --git a/lab-raziyeh/db/diagnostic.data/metrics.2016-10-29T05-14-51Z-00000 b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-29T05-14-51Z-00000 new file mode 100644 index 0000000..29fa144 Binary files /dev/null and b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-29T05-14-51Z-00000 differ diff --git a/lab-raziyeh/db/diagnostic.data/metrics.2016-10-31T20-19-37Z-00000 b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-31T20-19-37Z-00000 new file mode 100644 index 0000000..c154467 Binary files /dev/null and b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-31T20-19-37Z-00000 differ diff --git a/lab-raziyeh/db/diagnostic.data/metrics.2016-10-31T20-51-24Z-00000 b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-31T20-51-24Z-00000 new file mode 100644 index 0000000..859a203 Binary files /dev/null and b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-31T20-51-24Z-00000 differ diff --git a/lab-raziyeh/db/diagnostic.data/metrics.2016-10-31T21-10-31Z-00000 b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-31T21-10-31Z-00000 new file mode 100644 index 0000000..6a34f5a Binary files /dev/null and b/lab-raziyeh/db/diagnostic.data/metrics.2016-10-31T21-10-31Z-00000 differ diff --git a/lab-raziyeh/db/diagnostic.data/metrics.2016-11-06T08-48-31Z-00000 b/lab-raziyeh/db/diagnostic.data/metrics.2016-11-06T08-48-31Z-00000 new file mode 100644 index 0000000..6385f75 Binary files /dev/null and b/lab-raziyeh/db/diagnostic.data/metrics.2016-11-06T08-48-31Z-00000 differ diff --git a/lab-raziyeh/db/diagnostic.data/metrics.2016-11-07T22-34-23Z-00000 b/lab-raziyeh/db/diagnostic.data/metrics.2016-11-07T22-34-23Z-00000 new file mode 100644 index 0000000..b325988 Binary files /dev/null and b/lab-raziyeh/db/diagnostic.data/metrics.2016-11-07T22-34-23Z-00000 differ diff --git a/lab-raziyeh/db/diagnostic.data/metrics.interim b/lab-raziyeh/db/diagnostic.data/metrics.interim new file mode 100644 index 0000000..b45451b Binary files /dev/null and b/lab-raziyeh/db/diagnostic.data/metrics.interim differ diff --git a/lab-raziyeh/db/index-1--3143307283938210215.wt b/lab-raziyeh/db/index-1--3143307283938210215.wt new file mode 100644 index 0000000..f4b90fe Binary files /dev/null and b/lab-raziyeh/db/index-1--3143307283938210215.wt differ diff --git a/lab-raziyeh/db/index-1-6564260200608951873.wt b/lab-raziyeh/db/index-1-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/index-1-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/index-1-8845950676657731124.wt b/lab-raziyeh/db/index-1-8845950676657731124.wt new file mode 100644 index 0000000..dcac98c Binary files /dev/null and b/lab-raziyeh/db/index-1-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/index-10-6564260200608951873.wt b/lab-raziyeh/db/index-10-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/index-10-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/index-10-8845950676657731124.wt b/lab-raziyeh/db/index-10-8845950676657731124.wt new file mode 100644 index 0000000..56c5e08 Binary files /dev/null and b/lab-raziyeh/db/index-10-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/index-2-6564260200608951873.wt b/lab-raziyeh/db/index-2-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/index-2-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/index-2-8845950676657731124.wt b/lab-raziyeh/db/index-2-8845950676657731124.wt new file mode 100644 index 0000000..c14343c Binary files /dev/null and b/lab-raziyeh/db/index-2-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/index-4-6564260200608951873.wt b/lab-raziyeh/db/index-4-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/index-4-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/index-4-8845950676657731124.wt b/lab-raziyeh/db/index-4-8845950676657731124.wt new file mode 100644 index 0000000..1de98ca Binary files /dev/null and b/lab-raziyeh/db/index-4-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/index-5-6564260200608951873.wt b/lab-raziyeh/db/index-5-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/index-5-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/index-5-8845950676657731124.wt b/lab-raziyeh/db/index-5-8845950676657731124.wt new file mode 100644 index 0000000..8ccbd51 Binary files /dev/null and b/lab-raziyeh/db/index-5-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/index-6-6564260200608951873.wt b/lab-raziyeh/db/index-6-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/index-6-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/index-6-8845950676657731124.wt b/lab-raziyeh/db/index-6-8845950676657731124.wt new file mode 100644 index 0000000..d9dd9f3 Binary files /dev/null and b/lab-raziyeh/db/index-6-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/index-7-6564260200608951873.wt b/lab-raziyeh/db/index-7-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/index-7-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/index-7-8845950676657731124.wt b/lab-raziyeh/db/index-7-8845950676657731124.wt new file mode 100644 index 0000000..d8535e2 Binary files /dev/null and b/lab-raziyeh/db/index-7-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/index-8-6564260200608951873.wt b/lab-raziyeh/db/index-8-6564260200608951873.wt new file mode 100644 index 0000000..3f019cb Binary files /dev/null and b/lab-raziyeh/db/index-8-6564260200608951873.wt differ diff --git a/lab-raziyeh/db/index-8-8845950676657731124.wt b/lab-raziyeh/db/index-8-8845950676657731124.wt new file mode 100644 index 0000000..8c0a536 Binary files /dev/null and b/lab-raziyeh/db/index-8-8845950676657731124.wt differ diff --git a/lab-raziyeh/db/journal/WiredTigerLog.0000000008 b/lab-raziyeh/db/journal/WiredTigerLog.0000000008 new file mode 100644 index 0000000..58f776e Binary files /dev/null and b/lab-raziyeh/db/journal/WiredTigerLog.0000000008 differ diff --git a/lab-raziyeh/db/journal/WiredTigerPreplog.0000000001 b/lab-raziyeh/db/journal/WiredTigerPreplog.0000000001 new file mode 100644 index 0000000..170ccc6 Binary files /dev/null and b/lab-raziyeh/db/journal/WiredTigerPreplog.0000000001 differ diff --git a/lab-raziyeh/db/journal/WiredTigerPreplog.0000000002 b/lab-raziyeh/db/journal/WiredTigerPreplog.0000000002 new file mode 100644 index 0000000..170ccc6 Binary files /dev/null and b/lab-raziyeh/db/journal/WiredTigerPreplog.0000000002 differ diff --git a/lab-raziyeh/db/mongod.lock b/lab-raziyeh/db/mongod.lock new file mode 100644 index 0000000..ba3de0c --- /dev/null +++ b/lab-raziyeh/db/mongod.lock @@ -0,0 +1 @@ +61194 diff --git a/lab-raziyeh/db/sizeStorer.wt b/lab-raziyeh/db/sizeStorer.wt new file mode 100644 index 0000000..a647722 Binary files /dev/null and b/lab-raziyeh/db/sizeStorer.wt differ diff --git a/lab-raziyeh/db/storage.bson b/lab-raziyeh/db/storage.bson new file mode 100644 index 0000000..d07b1bc Binary files /dev/null and b/lab-raziyeh/db/storage.bson differ diff --git a/lab-raziyeh/karma.conf.js b/lab-raziyeh/karma.conf.js new file mode 100644 index 0000000..8827f97 --- /dev/null +++ b/lab-raziyeh/karma.conf.js @@ -0,0 +1,31 @@ +// Karma configuration +// Generated on Wed Oct 19 2016 09:49:07 GMT-0700 (PDT) + + +const webpack= require('./webpack.config.js'); +webpack.entry = {}; + +module.exports = function(config) { + config.set({ + webpack, + port: 9876, + colors: true, + basePath: '', + autoWatch: true, + singleRun: false, + concurrency: Infinity, + frameworks: ['jasmine'], + reporters: ['progress'], + browsers: ['PhantomJS'], + logLevel: config.LOG_INFO, + preprocessors: { + 'client-test/**/*-test.js': ['webpack'], + 'app/entry.js': ['webpack'], + }, + files: [ + 'app/entry.js', + 'client-test/**/*-test.js', + 'node_modules/angular-mocks/angular-mocks.js', + ], + }); +}; diff --git a/lab-raziyeh/lib/basic-auth-middleware.js b/lab-raziyeh/lib/basic-auth-middleware.js new file mode 100644 index 0000000..7feb9e8 --- /dev/null +++ b/lab-raziyeh/lib/basic-auth-middleware.js @@ -0,0 +1,27 @@ +'use strict' + +const createError = require('http-errors') +const debug = require('debug')('slugram:basic-auth-middleware') + +module.exports = function(req, res, next){ + debug() + + var authHeader = req.headers.authorization + if (!authHeader) return next(createError(401, 'requires authorization header')) + + let base64String = authHeader.split('Basic ')[1] + if (!base64String) return next(createError(401, 'require username and password')) + + let utf8String = new Buffer(base64String, 'base64').toString() + let authArray = utf8String.split(':') + req.auth = { + username: authArray[0], + password: authArray[1], + } + + if(!req.auth.username) return next(createError(401, 'basic auth requires username')) + if(!req.auth.password) return next(createError(401, 'basic auth requires password')) + next() +} + + diff --git a/lab-raziyeh/lib/bearer-auth-middleware.js b/lab-raziyeh/lib/bearer-auth-middleware.js new file mode 100644 index 0000000..d5dc431 --- /dev/null +++ b/lab-raziyeh/lib/bearer-auth-middleware.js @@ -0,0 +1,31 @@ +'use strict' + +// npm modules +const jwt = require('jsonwebtoken') +const createError = require('http-errors') +const debug = require('debug')('slugram:bearer-middleware') + +// app modules +const User = require('../model/user.js') + +module.exports = function(req, res, next){ + debug() + let authHeader = req.headers.authorization + if (!authHeader) + return next(createError(400, 'requires auth header')) + + let token = authHeader.split('Bearer ')[1] + if (!token) + return next(createError(400, 'requires token')) + + jwt.verify(token, process.env.APP_SECRET, (err, decoded) => { + if (err) + return next(createError(401, 'requires token')) + User.findOne({findHash: decoded.token}) + .then( user => { + if (!user) return next(createError(401, 'user no longer exists or has new token')) + req.user = user + next() + }) + }) +} diff --git a/lab-raziyeh/lib/error-middleware.js b/lab-raziyeh/lib/error-middleware.js new file mode 100644 index 0000000..97bcccd --- /dev/null +++ b/lab-raziyeh/lib/error-middleware.js @@ -0,0 +1,34 @@ +'use strict' + +const createError = require('http-errors') +const debug = require('debug')('slugram:error-middleware') + +module.exports = function(err, req, res, next){ + debug('error middleware') + debug('ERROR:', err.message) + + if (err.status){ + res.status(err.status).send(err.name) + next() + return + } + + if (err.name === 'ValidationError'){ + err = createError(400, err.message) + res.status(err.status).send(err.name) + next() + return + } + + if (err.name === 'MongoError' && err.message.startsWith('E11000 duplicate')){ + // 409 means conflict im using it for duplicate errors + err = createError(409, err.message) + res.status(err.status).send(err.name) + next() + return + } + + err = createError(500, err.message) + res.status(err.status).send(err.name) + next() +} diff --git a/lab-raziyeh/lib/fuzzy-query.js b/lab-raziyeh/lib/fuzzy-query.js new file mode 100644 index 0000000..942ad81 --- /dev/null +++ b/lab-raziyeh/lib/fuzzy-query.js @@ -0,0 +1,19 @@ +'use strict' + +let debug = require('debug')('slugram:fuzzy-query') + +let fuzzyRegex = require('./fuzzy-regex.js') + +module.exports = function(fieldnames, reqQuerys){ + debug('generating fuzzy queries') + if (!Array.isArray(fieldnames)) + return {} + if (typeof reqQuerys !== 'object') + return {} + let query = {} + fieldnames.forEach(val => { + if (reqQuerys[val]) + query[val] = {$regex: fuzzyRegex(reqQuerys[val])} + }) + return query +} diff --git a/lab-raziyeh/lib/fuzzy-regex.js b/lab-raziyeh/lib/fuzzy-regex.js new file mode 100644 index 0000000..ea66e0b --- /dev/null +++ b/lab-raziyeh/lib/fuzzy-regex.js @@ -0,0 +1,11 @@ +'use strict' + +const debug = require('debug')('slugram:fuzzy-regex') + +module.exports = function(input){ + debug(`creating fuzzy regex from ${input}`) + if (!input || typeof input !== 'string') + return new RegExp('.*') + let result = input.split('').join('.*') + return new RegExp(`.*${result}.*`) +} diff --git a/lab-raziyeh/lib/item-query-middleware.js b/lab-raziyeh/lib/item-query-middleware.js new file mode 100644 index 0000000..2fa0631 --- /dev/null +++ b/lab-raziyeh/lib/item-query-middleware.js @@ -0,0 +1,30 @@ +'use strict' + +const debug = require('debug')('slugram:query-middleware') + +module.exports = function(req, res, next){ + debug('creating itempage queries') + let itempage = 1 + let itemoffset = 0 + let itemcount = 100 + let itemsort = 1 + if (!isNaN(Number(req.query.itempage))) itempage = Number(req.query.itempage) + if (!isNaN(Number(req.query.itemoffset))) itemoffset = Number(req.query.itemoffset) + if (!isNaN(Number(req.query.itemcount))) itemcount = Number(req.query.itemcount) + if (req.query.itemsort === 'dsc') itemsort = -1 + if (req.query.itemsort === 'asc') itemsort = 1 + + if (itempage < 1) itempage = 1 + if (itemoffset < 0) itemoffset = 0 + if (itemcount < 1) itemcount = 1 + if (itemcount > 250) itemcount = 250 + + if (itempage > 1) + itemoffset = (itempage - 1) * itemcount + itemoffset + + req.query.itempage = itempage + req.query.itemoffset = itemoffset + req.query.itemcount = itemcount + req.query.itemsort = itemsort + next() +} diff --git a/lab-raziyeh/lib/page-query-middleware.js b/lab-raziyeh/lib/page-query-middleware.js new file mode 100644 index 0000000..ac9d8c0 --- /dev/null +++ b/lab-raziyeh/lib/page-query-middleware.js @@ -0,0 +1,30 @@ +'use strict' + +const debug = require('debug')('slugram:query-middleware') + +module.exports = function(req, res, next){ + debug('creating page queries') + let page = 1 + let offset = 0 + let pagesize = 50 + let sort = 1 + if (!isNaN(Number(req.query.page))) page = Number(req.query.page) + if (!isNaN(Number(req.query.offset))) offset = Number(req.query.offset) + if (!isNaN(Number(req.query.pagesize))) pagesize = Number(req.query.pagesize) + if (req.query.sort === 'dsc') sort = -1 + if (req.query.sort === 'asc') sort = 1 + + if (page < 1) page = 1 + if (offset < 0) offset = 0 + if (pagesize < 1) pagesize = 1 + if (pagesize > 250) pagesize = 250 + + if (page > 1) + offset = (page - 1) * pagesize + offset + + req.query.page = page + req.query.offset = offset + req.query.pagesize = pagesize + req.query.sort = sort + next() +} diff --git a/lab-raziyeh/lib/s3-upload-promise.js b/lab-raziyeh/lib/s3-upload-promise.js new file mode 100644 index 0000000..2d3fa80 --- /dev/null +++ b/lab-raziyeh/lib/s3-upload-promise.js @@ -0,0 +1,17 @@ +'use strict' + +const AWS = require('aws-sdk') +const debug = require('debug')('slugram:s3-upload-promise') + +const s3 = new AWS.S3() + +module.exports = function s3UploadPromise(params){ + debug('uploading file to s3') + return new Promise((resolve, reject) => { + s3.upload(params, (err, s3data) => { + if (err) + return reject(err) + resolve(s3data) + }) + }) +} diff --git a/lab-raziyeh/model/gallery.js b/lab-raziyeh/model/gallery.js new file mode 100644 index 0000000..7c311cd --- /dev/null +++ b/lab-raziyeh/model/gallery.js @@ -0,0 +1,14 @@ +'use strict' + +const mongoose = require('mongoose') + +const gallerySchema = mongoose.Schema({ + name: {type: String, required: true}, + desc: {type: String, required: true}, + username: {type: String, required: true}, + created: {type: Date, required: true, default: Date.now}, + userID: {type: mongoose.Schema.Types.ObjectId, required: true}, + pics: [{type: mongoose.Schema.Types.ObjectId, ref: 'pic'}], +}) + +module.exports = mongoose.model('gallery', gallerySchema) diff --git a/lab-raziyeh/model/pic.js b/lab-raziyeh/model/pic.js new file mode 100644 index 0000000..3e61cb4 --- /dev/null +++ b/lab-raziyeh/model/pic.js @@ -0,0 +1,20 @@ +'use strict' + +const mongoose = require('mongoose') + +const picSchema = mongoose.Schema({ + name: {type: String, required: true}, + desc: {type: String, required: true}, + username: {type: String, required: true}, + userID: {type: mongoose.Schema.Types.ObjectId, required: true}, + imageURI: {type: String, required: true, unique: true}, + objectKey: {type: String, required: true, unique: true}, + created: {type: Date, default: Date.now}, +}) + +module.exports = mongoose.model('pic', picSchema) + +//Pic.schema.path('name').validate(function(val) { + //return /(slug|byte)/.test(val) +//}) + diff --git a/lab-raziyeh/model/user.js b/lab-raziyeh/model/user.js new file mode 100644 index 0000000..a1a634b --- /dev/null +++ b/lab-raziyeh/model/user.js @@ -0,0 +1,78 @@ +'use strict' + +const crypto = require('crypto') +const bcrypt = require('bcrypt') +const jwt = require('jsonwebtoken') +const Promise = require('bluebird') +const mongoose = require('mongoose') +const createError = require('http-errors') +const debug = require('debug')('slugram:user') + +// mondule constant +const Schema = mongoose.Schema + +const userSchema = Schema({ + username: {type: String, required: true, unique: true, minlength: 5}, + email: {type: String, required: true, unique: true}, + password: {type: String, required: true}, + findHash: {type: String, unique: true}, +}) + +// for signup +// store a password that has been encrypted as a hash +userSchema.methods.generatePasswordHash = function(password){ + debug('generatePasswordHash') + return new Promise((resolve, reject) => { + bcrypt.hash(password, 10, (err, hash) => { + if (err) return reject(err) // 500 error + this.password = hash + resolve(this) + }) + }) +} + +// for signin +// compare a plain text password with the stored hashed password +userSchema.methods.comparePasswordHash = function(password){ + debug('comparePasswordHash') + return new Promise((resolve, reject) => { + bcrypt.compare(password, this.password, (err, valid) => { + if (err) return reject(err) // 500 error bcrypt failed + if (!valid) return reject(createError(401, 'wrong password')) + resolve(this) + }) + }) +} + +// for signup +userSchema.methods.generateFindHash = function(){ + debug('generateFindHash') + return new Promise((resolve, reject) => { + let tries = 0 + _generateFindHash.call(this) + + function _generateFindHash(){ + this.findHash = crypto.randomBytes(32).toString('hex') + this.save() + .then(() => resolve(this.findHash)) + .catch(err => { + if (tries > 3) return reject(err) // 500 error + tries++ + _generateFindHash.call(this) + }) + } + }) +} + +// for sinup and signin +userSchema.methods.generateToken = function(){ + debug('generateToken') + return new Promise((resolve, reject) => { + this.generateFindHash() + .then(findHash => resolve(jwt.sign({token: findHash}, process.env.APP_SECRET))) + .catch(err => reject(err)) // 500 error from find hash + }) +} + +module.exports = mongoose.model('user', userSchema) +// static methods diff --git a/lab-raziyeh/package.json b/lab-raziyeh/package.json new file mode 100644 index 0000000..49de439 --- /dev/null +++ b/lab-raziyeh/package.json @@ -0,0 +1,86 @@ +{ + "name": "basic-auth-demo", + "version": "1.0.0", + "description": "", + "main": "index.js", + "engines": { + "node": "4.4.7" + }, + "scripts": { + "build": "./node_modules/webpack/bin/webpack.js", + "build-watch": "./node_modules/webpack/bin/webpack.js -w", + "frontend-test": "./node_modules/karma/bin/karma start --single-run", + "test-watch": "./node_modules/karma/bin/karma start", + "start": "node server.js", + "start-debug": "DEBUG='slug*' npm start", + "start-watch": "DEBUG='slug*' ./node_modules/nodemon/bin/nodemon.js server.js", + "test": "./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha", + "coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls", + "lint": "./node_modules/eslint/bin/eslint.js .", + "test-debug": "DEBUG='slug*' ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "aws-sdk": "^2.6.6", + "bcrypt": "^0.8.7", + "bluebird": "^3.4.6", + "body-parser": "^1.15.2", + "cors": "^2.8.1", + "debug": "^2.2.0", + "del": "^2.2.2", + "dotenv": "^2.0.0", + "express": "^4.14.0", + "http-errors": "^1.5.0", + "jsonwebtoken": "^7.1.9", + "mongoose": "^4.6.2", + "morgan": "^1.7.0", + "multer": "^1.2.0", + "angular": "^1.5.8", + "angular-animate": "^1.5.8", + "angular-route": "^1.5.8", + "angular-touch": "^1.5.8", + "angular-ui-bootstrap": "^2.2.0", + "angular-ui-router": "^0.3.1", + "babel-core": "^6.17.0", + "babel-loader": "^6.2.5", + "babel-preset-es2015": "^6.16.0", + "bootstrap-sass": "^3.3.7", + "camelcase": "^3.0.0", + "clean-webpack-plugin": "^0.1.13", + "css-loader": "^0.25.0", + "extract-text-webpack-plugin": "^1.0.1", + "file-loader": "^0.9.0", + "font-awesome": "^4.6.3", + "html-loader": "^0.4.4", + "html-webpack-plugin": "^2.22.0", + "ng-file-upload": "^12.2.12", + "node-sass": "^3.10.1", + "pascalcase": "^0.1.1", + "resolve-url-loader": "^1.6.0", + "sass-loader": "^4.0.2", + "style-loader": "^0.13.1", + "ui-router": "^1.0.0-alpha.3", + "url-loader": "^0.5.7", + "webpack": "^1.13.2" + }, + "devDependencies": { + "angular-mocks": "^1.5.8", + "aws-sdk-mock": "^1.5.0", + "chai": "^3.5.0", + "coveralls": "^2.11.14", + "eslint": "^3.8.1", + "istanbul": "^0.4.5", + "jasmine-core": "^2.5.2", + "karma": "^1.3.0", + "karma-jasmine": "^1.0.2", + "karma-phantomjs-launcher": "^1.0.2", + "karma-webpack": "^1.8.0", + "lorem-ipsum": "^1.0.3", + "mocha": "^3.1.0", + "nodemon": "^1.11.0", + "superagent": "^2.3.0", + "webpack-dev-server": "^1.16.2" + } +} diff --git a/lab-raziyeh/route/auth-router.js b/lab-raziyeh/route/auth-router.js new file mode 100644 index 0000000..7127b1c --- /dev/null +++ b/lab-raziyeh/route/auth-router.js @@ -0,0 +1,43 @@ +'use strict' + +const Router = require('express').Router +const createError = require('http-errors') +const jsonParser = require('body-parser').json() +const debug = require('debug')('slugram:auth-router') +const basicAuth = require('../lib/basic-auth-middleware.js') +const User = require('../model/user.js') + +// module constants +const authRouter = module.exports = Router() + +authRouter.post('/api/signup', jsonParser, function(req, res, next){ + debug('POST /api/signup') + + let password = req.body.password + delete req.body.password + let user = new User(req.body) + + // checkfor password before running generatePasswordHash + if (!password) + return next(createError(400, 'requires password')) + if (password.length < 8) + return next(createError(400, 'password must be 8 characters')) + + user.generatePasswordHash(password) + .then( user => user.save()) // check for unique username with mongoose unique + .then( user => user.generateToken()) + .then( token => res.send(token)) + .catch(next) +}) + +authRouter.get('/api/login', basicAuth, function(req, res, next){ + debug('GET /api/login') + + User.findOne({username: req.auth.username}) + .then( user => user.comparePasswordHash(req.auth.password)) + .catch(err => Promise.reject(createError(401, err.message))) + .then( user => user.generateToken()) + .then( token => res.send(token)) + .catch(next) +}) + diff --git a/lab-raziyeh/route/gallery-router.js b/lab-raziyeh/route/gallery-router.js new file mode 100644 index 0000000..20ed222 --- /dev/null +++ b/lab-raziyeh/route/gallery-router.js @@ -0,0 +1,131 @@ +'use strict' + +// npm +const AWS = require('aws-sdk') +const Router = require('express').Router +const jsonParser = require('body-parser').json() +const createError = require('http-errors') +const debug = require('debug')('slugram:gallery-route') + +// app +const Pic = require('../model/pic.js') +const Gallery = require('../model/gallery.js') +const bearerAuth = require('../lib/bearer-auth-middleware.js') +const pageQueries = require('../lib/page-query-middleware.js') +const itemQueries = require('../lib/item-query-middleware.js') +const fuzzyQuery = require('../lib/fuzzy-query.js') + +// constants +const s3 = new AWS.S3() +const galleryRouter = module.exports = Router() + +galleryRouter.post('/api/gallery', bearerAuth, jsonParser, function(req, res, next){ + debug('POST /api/gallery') + req.body.userID = req.user._id + req.body.username = req.user.username + new Gallery(req.body).save() + .then( gallery => res.json(gallery)) + .catch(next) +}) + + +galleryRouter.get('/api/gallery/:id', bearerAuth, itemQueries, function(req, res, next){ + debug('GET /api/gallery/:id') + Gallery.findById(req.params.id) + .populate({ + path: 'pics', + options: { + sort: {_id: req.query.itemsort}, + limit: req.query.itemcount, + skip: req.query.itemoffset, + }, + }) + .catch(err => Promise.reject(createError(400, err.message))) + .then(gallery => { + if (gallery.userID.toString() !== req.user._id.toString()) + return Promise.reject(createError(401, 'invalid userid')) + res.json(gallery) + }) + .catch(next) +}) + +galleryRouter.put('/api/gallery/:id', bearerAuth, jsonParser, function(req, res, next){ + debug('PUT /api/gallery/:id') + Gallery.findById(req.params.id) + .catch(err => Promise.reject(createError(404, err.message))) + .then(gallery => { + if (gallery.userID.toString() !== req.user._id.toString()) + return Promise.reject(createError(401, 'not users gallery')) + let options = { runValidators: true, new: true} + return Gallery.findByIdAndUpdate(req.params.id, req.body, options) + }) + .then(gallery => res.json(gallery)) + .catch(next) +}) + +galleryRouter.delete('/api/gallery/:id', bearerAuth, function(req, res, next){ + debug('DELETE /api/gallery/:id') + let tempGallrey = null + Gallery.findById(req.params.id) + .populate('pics') + .catch(err => Promise.reject(createError(404, err.message))) + .then(gallery => { + tempGallrey = gallery + if (gallery.userID.toString() !== req.user._id.toString()) + return Promise.reject(createError(401, 'not users gallery')) + let deletePhotos = [] + + gallery.pics.forEach(pic => { + let params = { + Bucket: process.env.AWS_BUCKET, + Key: pic.objectKey, + } + deletePhotos.push(Pic.findByIdAndRemove(pic._id)) + deletePhotos.push(s3.deleteObject(params).promise()) + }) + + return Promise.all(deletePhotos) + }) + .then(() => tempGallrey.remove()) + .then(() => res.sendStatus(204)) + .catch(next) +}) + +galleryRouter.get('/api/gallery', bearerAuth, pageQueries, itemQueries, function(req, res, next){ + debug('GET /api/gallery') + + let fields = ['name', 'desc'] + let query = fuzzyQuery(fields, req.query) + query.userID = req.user._id.toString() + Gallery.find(query) + .populate({ + path: 'pics', + options: { + sort: {_id: req.query.itemsort}, + limit: req.query.itemcount, + skip: req.query.itemoffset, + }, + }) + .sort({_id: req.query.sort}).skip(req.query.offset).limit(req.query.pagesize) + .then(galleries => res.json(galleries)) + .catch(next) +}) + +// public anyone can call +galleryRouter.get('/api/public/gallery', pageQueries, itemQueries, function(req, res, next){ + let fields = ['username', 'name', 'desc'] + let query = fuzzyQuery(fields, req.query) + console.log('req.query.itemcount', req.query.itemcount) + Gallery.find(query) + .populate({ + path: 'pics', + options: { + sort: {_id: req.query.itemsort}, + limit: req.query.itemcount, + skip: req.query.itemoffset, + }, + }) + .sort({_id: req.query.sort}).skip(req.query.offset).limit(req.query.pagesize) + .then(galleries => res.json(galleries)) + .catch(next) +}) diff --git a/lab-raziyeh/route/pic-router.js b/lab-raziyeh/route/pic-router.js new file mode 100644 index 0000000..d725d62 --- /dev/null +++ b/lab-raziyeh/route/pic-router.js @@ -0,0 +1,146 @@ +'use strict' + +// node module +const fs = require('fs') +const path = require('path') + +// npm module +const del = require('del') +const AWS = require('aws-sdk') +const multer = require('multer') +const createError = require('http-errors') +const debug = require('debug')('sulgram:pic-router') + +// app module +const Pic = require('../model/pic.js') +const Gallery = require('../model/gallery.js') +const fuzzyQuery = require('../lib/fuzzy-query.js') +const bearerAuth = require('../lib/bearer-auth-middleware.js') +const pageQuery = require('../lib/page-query-middleware.js') + +// Use bluebird implementation of Promise +// will add a .promise() to AWS.Request +AWS.config.setPromisesDependency(require('bluebird')) + +// module constants +const s3 = new AWS.S3() +const dataDir =`${__dirname}/../data` +const upload = multer({dest: dataDir }) +const s3UploadPromise = require('../lib/s3-upload-promise.js') +const picRouter = module.exports = require('express').Router() + + +picRouter.post('/api/gallery/:galleryID/pic', bearerAuth, upload.single('file'), function(req, res, next){ + debug('POST /api/gallery/:galleryID/pic') + if(!req.file) + return next(createError(400, 'no file found')) + + let ext = path.extname(req.file.originalname) // '.png' | '.gif' | '.tar.gz' + + let params = { + ACL: 'public-read', + Bucket: process.env.AWS_BUCKET, + Key: `${req.file.filename}${ext}`, + Body: fs.createReadStream(req.file.path), + } + + // first check that the gallery exists + // then upload image + // remove the image that multer stored on the local disk + // then store monogo Pic + // then respond to user + + let tempGallery, tempPic + Gallery.findById(req.params.galleryID) + .catch(err => Promise.reject(createError(404, err.message))) + .then(gallery => { + tempGallery = gallery + return s3UploadPromise(params)// IF FAILS 500 ERROR + }) + .catch(err => err.status ? Promise.reject(err) : Promise.reject(createError(500, err.message))) + .then(s3data => { + del([`${dataDir}/*`]) + let picData = { + name: req.body.name, + username: req.user.username, + desc: req.body.desc, + objectKey: s3data.Key, + imageURI: s3data.Location, + userID: req.user._id, + } + return new Pic(picData).save() + }) + .then(pic => { + tempPic = pic + tempGallery.pics.push(pic._id) + return tempGallery.save() + }) + .then(() => res.json(tempPic)) + .catch(err => { + del([`${dataDir}/*`]) + next(err) + }) +}) + +picRouter.delete('/api/gallery/:galleryID/pic/:picID', bearerAuth, function(req, res, next){ + debug('DELETE /api/gallery/:galleryID/pic/:picID') + + + //check that the pic exists if not 404 + //make sure there userID matches the pic.userID if not 401 + //check that gallery id is correct if not 404 + //remove the picID from the gallery + //delete the picture from aws + //delete the pic from mongo + //respond to the client + let tempPic + Pic.findById(req.params.picID) // 404 + .then( pic => { + if(pic.userID.toString() !== req.user._id.toString()) + return Promise.reject(createError(401, 'user not authtorized to delete this pic')) + tempPic = pic + return Gallery.findById(req.params.galleryID) // 404 + }) + .catch(err => err.status? Promise.reject(err) : Promise.reject(createError(404, err.message))) // if no pic or gal found + .then( gallery => { + gallery.pics = gallery.pics.filter( id => { + if (id === req.params.picID) return false + return true + }) + return gallery.save() // 500 error + }) + .then(() => { + let params = { + Bucket: process.env.AWS_BUCKET, + Key: tempPic.objectKey, + } + return s3.deleteObject(params).promise() // 500 error + }) + .then(() => { + return Pic.findByIdAndRemove(req.params.picID) //500 + }) + .then(() => res.sendStatus(204)) + .catch(next) +}) + +picRouter.get('/api/public/pic', pageQuery, function(req, res, next){ + let fields = ['username', 'name', 'desc'] + let query = fuzzyQuery(fields, req.query) + + Pic.find(query) + .sort({_id: req.query.sort}).skip(req.query.offset).limit(req.query.pagesize) + .then(pics => res.json(pics)) + .catch(next) +}) + + // this route is private and only returns a users pictures +picRouter.get('/api/pic', bearerAuth, pageQuery, function(req, res, next){ + let fuzzyFields = [ 'name', 'desc' ] + let query = fuzzyQuery(fuzzyFields, req.query) + query.userID = req.user._id.toString() + Pic.find(query) + .sort({_id: req.query.sort}).skip(req.query.offset).limit(req.query.pagesize) + .then(pics => res.json(pics)) + .catch(next) +}) + diff --git a/lab-raziyeh/server.js b/lab-raziyeh/server.js new file mode 100644 index 0000000..e8bacf1 --- /dev/null +++ b/lab-raziyeh/server.js @@ -0,0 +1,48 @@ +'use strict' + +// npm modules +const cors = require('cors') +const dotenv = require('dotenv') +const morgan = require('morgan') +const express = require('express') +const Promise = require('bluebird') +const mongoose = require('mongoose') +const debug = require('debug')('slugram:sever') + +// app modules +const picRouter = require('./route/pic-router.js') +const authRouter = require('./route/auth-router.js') +const galleryRouter = require('./route/gallery-router.js') +const errorMiddleware = require('./lib/error-middleware.js') + +// load env vars +dotenv.load({path: `${__dirname}/.server.env`}) + +// setup mongoose +mongoose.Promise = Promise +mongoose.connect(process.env.MONGODB_URI); + +// module constants +const PORT = process.env.PORT +const app = express() + + +// app middleware +app.use(cors()) +let production = process.env.NODE_ENV === 'production' +let morganFormat = production ? 'common' : 'dev' +app.use(morgan(morganFormat)) + +// app routes +app.use(express.static(`${__dirname}/build`)); +app.use(picRouter) +app.use(authRouter) +app.use(galleryRouter) +app.use(errorMiddleware) + +// start server +const server = module.exports = app.listen(PORT , () => { + debug(`server up on ${PORT}`) +}) + +server.isRunning = true diff --git a/lab-raziyeh/test/auth-router-test.js b/lab-raziyeh/test/auth-router-test.js new file mode 100644 index 0000000..c965da8 --- /dev/null +++ b/lab-raziyeh/test/auth-router-test.js @@ -0,0 +1,219 @@ +'use strict' + +require('./lib/test-env.js') +require('./lib/aws-mocks.js') +const expect = require('chai').expect +const request = require('superagent') +const Promise = require('bluebird') +const mongoose = require('mongoose') +const serverCtrl = require('./lib/server-ctrl.js') +const cleanDB = require('./lib/clean-db.js') +const mockUser = require('./lib/user-mock.js') + +mongoose.Promise = Promise + +const server = require('../server.js') +const url = `http://localhost:${process.env.PORT}` + +const exampleUser = { + username: 'slugbyte', + password: '12345678', + email: 'slug@slug.slime', +} + +describe('testing auth-router', function(){ + // start server at for this test file + before(done => serverCtrl.serverUp(server, done)) + // stop server after this test file + after(done => serverCtrl.serverDown(server, done)) + // clean all models from db after each test + afterEach(done => cleanDB(done)) + + describe('testing POST /api/signup', function(){ + describe('with valid body', function(){ + it('should return a token', (done) => { + request.post(`${url}/api/signup`) + .send(exampleUser) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(!!res.text).to.equal(true) + done() + }) + }) + }) + + describe('with no username', function(){ + it('should respond with status 400', (done) => { + request.post(`${url}/api/signup`) + .send({ + password: exampleUser.password, + email: exampleUser.email, + }) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with username < 5', function(){ + it('should respond with status 400', (done) => { + request.post(`${url}/api/signup`) + .send({ + username: 'slug', + password: exampleUser.password, + email: exampleUser.email, + }) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with duplicate username', function(){ + before( done => mockUser.call(this, done)) + it('should respond with status 409', (done) => { + request.post(`${url}/api/signup`) + .send({ + username: this.tempUser.username, + password: exampleUser.password, + email: exampleUser.email, + }) + .end((err, res) => { + expect(res.status).to.equal(409) + expect(res.text).to.equal('ConflictError') + done() + }) + }) + }) + + describe('with duplicate email', function(){ + before( done => mockUser.call(this, done)) + it('should respond with status 409', (done) => { + request.post(`${url}/api/signup`) + .send({ + username: exampleUser.username, + password: exampleUser.password, + email: this.tempUser.email, + }) + .end((err, res) => { + expect(res.status).to.equal(409) + expect(res.text).to.equal('ConflictError') + done() + }) + }) + }) + + + describe('with no email', function(){ + it('should respond with status 400', (done) => { + request.post(`${url}/api/signup`) + .send({ + username: exampleUser.username, + password: exampleUser.password, + }) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with no password', function(){ + it('should respond with status 400', (done) => { + request.post(`${url}/api/signup`) + .send({ + email: exampleUser.email, + username: exampleUser.username, + }) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with password.length < 8', function(){ + it('should respond with status 400', (done) => { + request.post(`${url}/api/signup`) + .send({ + email: exampleUser.email, + password: '124567', + username: exampleUser.username, + }) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + }) + + describe('testing GET /api/signup', function(){ + describe('with valid auth', function(){ + before(done => mockUser.call(this, done)) + it('should return a token', (done) => { + request.get(`${url}/api/login`) + // this has to be the same user and pass from mockUser + .auth(this.tempUser.username, this.tempPassword) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(!!res.text).to.equal(true) + done() + }) + }) + }) + + describe('with bad username', function(){ + before(done => mockUser.call(this, done)) + it('should respond with status 401', (done) => { + request.get(`${url}/api/login`) + // this has to be the same user and pass from mockUser + .auth('bad', this.tempPassword) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('with bad username', function(){ + before(done => mockUser.call(this, done)) + it('should respond with status 401', (done) => { + request.get(`${url}/api/login`) + // this has to be the same user and pass from mockUser + .auth('', this.tempPassword) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('with bad password', function(){ + before(done => mockUser.call(this, done)) + it('should respond with status 401', (done) => { + request.get(`${url}/api/login`) + // this has to be the same user and pass from mockUser + .auth(this.tempUser.username, 'bad') + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + }) +}) diff --git a/lab-raziyeh/test/data/shield.png b/lab-raziyeh/test/data/shield.png new file mode 100644 index 0000000..42969d0 Binary files /dev/null and b/lab-raziyeh/test/data/shield.png differ diff --git a/lab-raziyeh/test/error-middleware-test.js b/lab-raziyeh/test/error-middleware-test.js new file mode 100644 index 0000000..262ca7c --- /dev/null +++ b/lab-raziyeh/test/error-middleware-test.js @@ -0,0 +1,84 @@ +'use strict' + +require('./lib/test-env.js') +require('./lib/aws-mocks.js') + +const expect = require('chai').expect +const errorMiddleware = require('../lib/error-middleware.js') +const createError = require('http-errors') + +function mockRes (){ + let res = {} + res.status = function(num){ + this.statusCode = num + return this + } + res.send = function(data){ + this.text = data.toString() + return this + } + res.json = function(data){ + this.body = data + return this + } + + return res +} + +describe('testing errorMiddleware', function(){ + describe('with vanilla Error', function(){ + let res + before(() => res = mockRes()) + it('should respond with status 500', done => { + let err = new Error('ordinary error') + errorMiddleware(err, {}, res, () => { + expect(res.statusCode).to.equal(500) + expect(res.text).to.equal('InternalServerError') + done() + }) + }) + }) + + describe('http-errors error', function(){ + let res + before(() => res = mockRes()) + it('should respond with status 418', done => { + let err = createError(418, 'example error message') + errorMiddleware(err, {}, res, () => { + expect(res.statusCode).to.equal(418) + expect(res.text).to.equal('ImATeapotError') + done() + }) + }) + }) + + describe('with ValidationError', function(){ + let res + before(() => res = mockRes()) + it('should respond with status 418', done => { + let err = new Error('You should read the docs') + err.name = 'ValidationError' + errorMiddleware(err, {}, res, () => { + expect(res.statusCode).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with MongoError && err.message = "E11000 duplicate"', function(){ + let res + before(() => res = mockRes()) + it('should respond with status 409', done => { + let err = new Error('E11000 duplicate') + err.name = 'MongoError' + errorMiddleware(err, {}, res, () => { + expect(res.statusCode).to.equal(409) + expect(res.text).to.equal('ConflictError') + done() + }) + }) + }) +}) + + diff --git a/lab-raziyeh/test/fuzzy-query-test.js b/lab-raziyeh/test/fuzzy-query-test.js new file mode 100644 index 0000000..9859684 --- /dev/null +++ b/lab-raziyeh/test/fuzzy-query-test.js @@ -0,0 +1,47 @@ +'use strict' + +const expect = require('chai').expect +const fuzzyQuery = require('../lib/fuzzy-query.js') + +describe('testing module fuzzy-query', function(){ + describe('with valid input', function(){ + let fields = ['name', 'duck'] + let query = { + name: 'slug', + duck: 'quack', + } + + it('should return a mongo query', done => { + let result = fuzzyQuery(fields, query) + console.log('result', result) + expect(result.name['$regex'].toString()).to.equal('/.*s.*l.*u.*g.*/') + expect(result.duck.$regex.toString()).to.equal('/.*q.*u.*a.*c.*k.*/') + done() + }) + }) + + describe('with bad array', function(){ + let fields = '' + let query = { + name: 'slug', + duck: 'quack', + } + + it('should return a mongo query', done => { + let result = fuzzyQuery(fields, query) + expect(JSON.stringify(result)).to.equal('{}') + done() + }) + }) + + describe('with bad array', function(){ + let fields = [] + let query = '' + + it('should return a mongo query', done => { + let result = fuzzyQuery(fields, query) + expect(JSON.stringify(result)).to.equal('{}') + done() + }) + }) +}) diff --git a/lab-raziyeh/test/fuzzy-regex-test.js b/lab-raziyeh/test/fuzzy-regex-test.js new file mode 100644 index 0000000..183f48f --- /dev/null +++ b/lab-raziyeh/test/fuzzy-regex-test.js @@ -0,0 +1,30 @@ +'use strict' + +const expect = require('chai').expect +const fuzzyRegex = require('../lib/fuzzy-regex.js') + +describe('testing module fuzzy-regex', function(){ + describe('with string "yep"', function(){ + it('should return /.*y.*e.*p.*/', done => { + let exp = fuzzyRegex('yep') + expect(exp.toString()).to.equal('/.*y.*e.*p.*/') + done() + }) + }) + + describe('with no input', function(){ + it('should return /.*/', done => { + let exp = fuzzyRegex() + expect(exp.toString()).to.equal('/.*/') + done() + }) + }) + + describe('with non string input', function(){ + it('should return /.*/', done => { + let exp = fuzzyRegex(4) + expect(exp.toString()).to.equal('/.*/') + done() + }) + }) +}) diff --git a/lab-raziyeh/test/gallery-router-test.js b/lab-raziyeh/test/gallery-router-test.js new file mode 100644 index 0000000..71db951 --- /dev/null +++ b/lab-raziyeh/test/gallery-router-test.js @@ -0,0 +1,969 @@ +'use strict' + +require('./lib/test-env.js') +require('./lib/aws-mocks.js') + +// npm +const expect = require('chai').expect +const request = require('superagent') +const mongoose = require('mongoose') +const Promise = require('bluebird') + +// app +const User = require('../model/user.js') +const server = require('../server.js') +const cleanDB = require('./lib/clean-db.js') +const mockUser = require('./lib/user-mock.js') +const serverCtrl = require('./lib/server-ctrl.js') +const fuzzyRegex = require('../lib/fuzzy-regex.js') +const mockGallery = require('./lib/gallery-mock.js') +const mockManyPics = require('./lib/mock-many-pics.js') +const mockManyGallerys = require('./lib/mock-many-gallerys.js') +//const mockManyEverything = require('./lib/mock-many-everything.js') +const mockManyEverything = require('./lib/everything-mock.js') + +// const +const url = `http://localhost:${process.env.PORT}` + // config +mongoose.Promise = Promise +let exampleGallery = { + name: 'beach adventure', + desc: 'not enough sun screan ouch', +} + +describe('test /api/gallery', function(){ + // start server at for this test file + before(done => serverCtrl.serverUp(server, done)) + // stop server after this test file + after(done => serverCtrl.serverDown(server, done)) + // clean all models from db after each test + afterEach(done => cleanDB(done)) + + describe('testing POST to /api/gallery', () => { + // create this.tempUser and this.tempToken + describe('with valid token and body', () => { + before(done => mockUser.call(this, done)) + it('should return a gallery', done => { + request.post(`${url}/api/gallery`) + .send(exampleGallery) + .set({Authorization: `Bearer ${this.tempToken}`}) + .end((err, res) => { + if (err) + return done(err) + expect(res.body.name).to.equal(exampleGallery.name) + expect(res.body.desc).to.equal(exampleGallery.desc) + expect(res.body.userID).to.equal(this.tempUser._id.toString()) + let date = new Date(res.body.created).toString() + expect(date).to.not.equal('Invalid Date') + done() + }) + }) + }) + + describe('with invalid token', () => { + before(done => mockUser.call(this, done)) + it('should respond with status 401', done => { + request.post(`${url}/api/gallery`) + .send(exampleGallery) + .set({Authorization: `Bearer ${this.tempToken}bad`}) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('with invalid Bearer auth', () => { + before(done => mockUser.call(this, done)) + it('should respond with status 400', done => { + request.post(`${url}/api/gallery`) + .send(exampleGallery) + .set({Authorization: 'not bearer auth'}) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with no Authorization header', () => { + before(done => mockUser.call(this, done)) + it('should respond with status 400', done => { + request.post(`${url}/api/gallery`) + .send(exampleGallery) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with no name', () => { + before(done => mockUser.call(this, done)) + it('should respond with status 400', done => { + request.post(`${url}/api/gallery`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .send({ desc: exampleGallery.desc}) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with no desc', () => { + before(done => mockUser.call(this, done)) + it('should respond with status 400', done => { + request.post(`${url}/api/gallery`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .send({ name: exampleGallery.name}) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + }) + + describe('testing GET to /api/gallery/:id', () => { + // create this.tempToken, this.tempUser, this.tempGallery + describe('with valid token and id', function(){ + before(done => mockGallery.call(this, done)) + it('should return a gallery', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if (err) + return done(err) + expect(res.body.name).to.equal(exampleGallery.name) + expect(res.body.desc).to.equal(exampleGallery.desc) + expect(res.body.userID).to.equal(this.tempUser._id.toString()) + let date = new Date(res.body.created).toString() + expect(date).to.equal(this.tempGallery.created.toString()) + done() + }) + }) + }) + + describe('with many pictures', function(){ + before(done => mockManyPics.call(this, 100, done)) + it('should return a gallery', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if (err) + return done(err) + expect(res.body.name).to.equal(exampleGallery.name) + expect(res.body.desc).to.equal(exampleGallery.desc) + expect(res.body.userID).to.equal(this.tempUser._id.toString()) + expect(Array.isArray(res.body.pics)).to.equal(true) + expect(res.body.pics.length).to.equal(100) + let date = new Date(res.body.created).toString() + expect(date).to.equal(this.tempGallery.created.toString()) + for (let i=0; i< res.body.pics.length; i++){ + expect(res.body.pics[i]._id.toString()).to.equal(this.tempPics[i]._id.toString()) + expect(res.body.pics[i].name).to.equal(this.tempPics[i].name) + expect(res.body.pics[i].desc).to.equal(this.tempPics[i].desc) + expect(res.body.pics[i].imageURI).to.equal(this.tempPics[i].imageURI) + } + done() + }) + }) + }) + + describe('with ?itemcount=10&itempage=2', function(){ + before(done => mockManyPics.call(this, 100, done)) + it('should return a gallery', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}?itemcount=10&itempage=2`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if (err) + return done(err) + expect(res.body.name).to.equal(exampleGallery.name) + expect(res.body.desc).to.equal(exampleGallery.desc) + expect(res.body.userID).to.equal(this.tempUser._id.toString()) + expect(Array.isArray(res.body.pics)).to.equal(true) + expect(res.body.pics.length).to.equal(10) + let date = new Date(res.body.created).toString() + expect(date).to.equal(this.tempGallery.created.toString()) + for (let i=0; i< res.body.pics.length; i++){ + expect(res.body.pics[i]._id.toString()).to.equal(this.tempPics[i + 10 ]._id.toString()) + expect(res.body.pics[i].name).to.equal(this.tempPics[i + 10].name) + expect(res.body.pics[i].desc).to.equal(this.tempPics[i + 10].desc) + expect(res.body.pics[i].imageURI).to.equal(this.tempPics[i + 10].imageURI) + } + done() + }) + }) + }) + + describe('with many pictures and ?itemcount=10', function(){ + before(done => mockManyPics.call(this, 100, done)) + it('should return a gallery', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}?itemcount=10`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if (err) + return done(err) + expect(res.body.name).to.equal(exampleGallery.name) + expect(res.body.desc).to.equal(exampleGallery.desc) + expect(res.body.userID).to.equal(this.tempUser._id.toString()) + expect(Array.isArray(res.body.pics)).to.equal(true) + expect(res.body.pics.length).to.equal(10) + let date = new Date(res.body.created).toString() + expect(date).to.equal(this.tempGallery.created.toString()) + for (let i=0; i< res.body.pics.length; i++){ + expect(res.body.pics[i]._id.toString()).to.equal(this.tempPics[i]._id.toString()) + expect(res.body.pics[i].name).to.equal(this.tempPics[i].name) + expect(res.body.pics[i].desc).to.equal(this.tempPics[i].desc) + expect(res.body.pics[i].imageURI).to.equal(this.tempPics[i].imageURI) + } + done() + }) + }) + }) + + describe('with many pictures and ?itemcount=10&itemsort=dsc', function(){ + before(done => mockManyPics.call(this, 100, done)) + it('should return a gallery', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}?itemcount=10&itemsort=dsc`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if (err) + return done(err) + expect(res.body.name).to.equal(exampleGallery.name) + expect(res.body.desc).to.equal(exampleGallery.desc) + expect(res.body.userID).to.equal(this.tempUser._id.toString()) + expect(Array.isArray(res.body.pics)).to.equal(true) + expect(res.body.pics.length).to.equal(10) + let date = new Date(res.body.created).toString() + expect(date).to.equal(this.tempGallery.created.toString()) + let tempPicsLength = this.tempPics.length + for (let i=0; i< res.body.pics.length; i++){ + expect(res.body.pics[i]._id.toString()).to.equal(this.tempPics[tempPicsLength - 1 - i]._id.toString()) + expect(res.body.pics[i].name).to.equal(this.tempPics[tempPicsLength - 1 - i].name) + expect(res.body.pics[i].desc).to.equal(this.tempPics[tempPicsLength - 1 - i].desc) + expect(res.body.pics[i].imageURI).to.equal(this.tempPics[tempPicsLength - 1 - i].imageURI) + } + done() + }) + }) + }) + + describe('with many pictures and ?itemcount=10&itemsort=dsc?itemoffset=1', function(){ + before(done => mockManyPics.call(this, 100, done)) + it('should return a gallery', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}?itemcount=10&itemsort=dsc&itemoffset=1`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if (err) + return done(err) + expect(res.body.name).to.equal(exampleGallery.name) + expect(res.body.desc).to.equal(exampleGallery.desc) + expect(res.body.userID).to.equal(this.tempUser._id.toString()) + expect(Array.isArray(res.body.pics)).to.equal(true) + expect(res.body.pics.length).to.equal(10) + let date = new Date(res.body.created).toString() + expect(date).to.equal(this.tempGallery.created.toString()) + let tempPicsLength = this.tempPics.length + for (let i=0; i< res.body.pics.length; i++){ + expect(res.body.pics[i]._id.toString()).to.equal(this.tempPics[tempPicsLength - 2 - i]._id.toString()) + expect(res.body.pics[i].name).to.equal(this.tempPics[tempPicsLength - 2 - i].name) + expect(res.body.pics[i].desc).to.equal(this.tempPics[tempPicsLength - 2 - i].desc) + expect(res.body.pics[i].imageURI).to.equal(this.tempPics[tempPicsLength - 2 - i].imageURI) + } + done() + }) + }) + }) + + describe('with many pictures and ?itemcount=10&itemoffset=1', function(){ + before(done => mockManyPics.call(this, 100, done)) + it('should return a gallery', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}?itemcount=10&itemoffset=1`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if (err) + return done(err) + expect(res.body.name).to.equal(exampleGallery.name) + expect(res.body.desc).to.equal(exampleGallery.desc) + expect(res.body.userID).to.equal(this.tempUser._id.toString()) + expect(Array.isArray(res.body.pics)).to.equal(true) + expect(res.body.pics.length).to.equal(10) + let date = new Date(res.body.created).toString() + expect(date).to.equal(this.tempGallery.created.toString()) + for (let i=0; i< res.body.pics.length; i++){ + expect(res.body.pics[i]._id.toString()).to.equal(this.tempPics[i + 1]._id.toString()) + expect(res.body.pics[i].name).to.equal(this.tempPics[i + 1].name) + expect(res.body.pics[i].desc).to.equal(this.tempPics[i + 1].desc) + expect(res.body.pics[i].imageURI).to.equal(this.tempPics[i + 1].imageURI) + } + done() + }) + }) + }) + + describe('with invalid token', function(){ + before(done => mockGallery.call(this, done)) + it('should respond with status 401', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}bad`, + }) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('with invalid Bearer auth', function(){ + before(done => mockGallery.call(this, done)) + it('should respond with status 400', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ Authorization: 'bad request' }) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with no Authorization header', function(){ + before(done => mockGallery.call(this, done)) + it('should respond with status 400', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with invalid id', function(){ + before(done => mockGallery.call(this, done)) + it('should return a gallery', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}bad`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with user whos been removed', function(){ + before(done => mockGallery.call(this, done)) + before(done => { + User.remove({}) + .then(() => done()) + .catch(done) + }) + + it('should respond with status 401', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('with wrong user', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + // overwrite user, password, and token with new user + before(done => mockUser.call(this, done)) + + it('should respond with status 401', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + }) + + describe('testing PUT /api/gallery/:galleryID', function(){ + describe('update name ande desc', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + + it('should return a gallery', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .send({ + name: 'hello', + desc: 'cool', + }) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if(err) return done(err) + expect(res.status).to.equal(200) + expect(res.body.name).to.equal('hello') + expect(res.body.desc).to.equal('cool') + done() + }) + }) + }) + + describe('update name ande desc', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + before(done => mockUser.call(this, done)) + + it('should return a gallery', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .send({ + name: 'hello', + desc: 'cool', + }) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + expect(res.status).to.equal(401) + done() + }) + }) + }) + + describe('update name', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + + it('should return a gallery', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .send({ + name: 'hello', + }) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if (err) return done(err) + expect(res.status).to.equal(200) + expect(res.body.name).to.equal('hello') + expect(res.body.desc).to.equal(this.tempGallery.desc) + done() + }) + }) + }) + + describe('update desc', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + + it('should return a gallery', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .send({ + desc: 'cool', + }) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + if (err) return done(err) + expect(res.status).to.equal(200) + expect(res.body.name).to.equal(this.tempGallery.name) + expect(res.body.desc).to.equal('cool') + done() + }) + }) + }) + + describe('with bad galeryID', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + + it('should return a gallery', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}bad`) + .send({ + desc: 'cool', + }) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + expect(res.status).to.equal(404) + expect(res.text).to.equal('NotFoundError') + done() + }) + }) + }) + + describe('with bad token', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + + it('should respond with status 401', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .send({ + desc: 'cool', + }) + .set({ + Authorization: `Bearer ${this.tempToken}bad`, + }) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('witn no auth', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + + it('should respond with status 400', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}bad`) + .send({ + desc: 'cool', + }) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + }) + + describe('testing DELETE /api/gallery/:galleryID', function(){ + describe('should respond with status 204', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + it('should return a gallery', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}`, + }) + .end((err, res) => { + expect(res.status).to.equal(204) + done() + }) + }) + }) + + describe('with invalid galleryID', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + it('should return a gallery', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}bad`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(404) + expect(res.text).to.equal('NotFoundError') + done() + }) + }) + }) + + describe('with invalid galleryID', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + before(done => mockUser.call(this, done)) + it('should return a gallery', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(401) + done() + }) + }) + }) + + describe('with invalid token', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + it('should respond with status 401', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}bad`, + }) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('witn no auth', function(){ + // mock user, password, token, and gallery + before(done => mockGallery.call(this, done)) + it('should respond with status 400', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}`) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + }) + + describe('testing GET /api/gallery', function(){ + describe('with valid request', function(){ + before( done => mockManyGallerys.call(this, 100, done)) + it('should respond with status 400', done => { + request.get(`${url}/api/gallery`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(50) + done() + }) + }) + }) + + describe('with ?pagesize=10', function(){ + before( done => mockManyGallerys.call(this, 100, done)) + it('should return 10 notes', done => { + request.get(`${url}/api/gallery?pagesize=5`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(5) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.equal(this.tempGallerys[i].name) + } + done() + }) + }) + }) + + describe('with ?sort=dsc', function(){ + before( done => mockManyGallerys.call(this, 100, done)) + it('should return 10 notes', done => { + request.get(`${url}/api/gallery?sort=dsc`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(50) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.equal(this.tempGallerys[this.tempGallerys.length - i - 1].name) + } + done() + }) + }) + }) + + describe('with ?sort=dsc?offset=3', function(){ + before( done => mockManyGallerys.call(this, 100, done)) + it('should return 10 notes', done => { + request.get(`${url}/api/gallery?sort=dsc&offset=3`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(50) + for (let i=0; i < res.body.length; i++){ + let index = this.tempGallerys.length - i - 4 + expect(res.body[i].name).to.equal(this.tempGallerys[index].name) + } + done() + }) + }) + }) + + describe('with offset=1', function(){ + before( done => mockManyGallerys.call(this, 100, done)) + it('should return 10 notes', done => { + request.get(`${url}/api/gallery?offset=1`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(50) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.equal(this.tempGallerys[i + 1].name) + } + done() + }) + }) + }) + + describe('with ?page=2', function(){ + before( done => mockManyGallerys.call(this, 100, done)) + it('should return 10 notes', done => { + request.get(`${url}/api/gallery?page=2`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(50) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.equal(this.tempGallerys[i + 50].name) + } + done() + }) + }) + }) + + describe('with ?page=3&?offset=1', function(){ + before( done => mockManyGallerys.call(this, 150, done)) + it('should return 10 notes', done => { + request.get(`${url}/api/gallery?page=3&offset=1`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(49) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.equal(this.tempGallerys[i + 101].name) + } + done() + }) + }) + }) + + describe('with ?page=-1', function(){ + before( done => mockManyGallerys.call(this, 150, done)) + it('should return 10 notes', done => { + request.get(`${url}/api/gallery?page=-1`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(50) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.equal(this.tempGallerys[i].name) + } + done() + }) + }) + }) + + describe('with ?pagesize=-1', function(){ + before( done => mockManyGallerys.call(this, 50, done)) + it('should return 10 notes', done => { + request.get(`${url}/api/gallery?pagesize=-1`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(1) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.equal(this.tempGallerys[i].name) + } + done() + }) + }) + }) + + describe('with ?pagesize=300', function(){ + before( done => mockManyGallerys.call(this, 300, done)) + it('should return 10 notes', done => { + request.get(`${url}/api/gallery?pagesize=250`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(250) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.equal(this.tempGallerys[i].name) + } + done() + }) + }) + }) + + describe('with invalid token', function(){ + before( done => mockManyGallerys.call(this, 50, done)) + it('should respond with status 401', done => { + request.get(`${url}/api/gallery`) + .set({ Authorization: `Bearer ${this.tempToken}bad` }) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('with ?name=co', function(){ + before( done => mockManyGallerys.call(this, 100, done)) + it('should respond nodes with fuzzy match co', done => { + request.get(`${url}/api/gallery?name=co`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + console.log('matching notes', res.body.length) + let fuzzyName = fuzzyRegex('co') + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.match(fuzzyName) + } + done() + }) + }) + }) + + describe('with ?desc=co', function(){ + before( done => mockManyGallerys.call(this, 100, done)) + it('should respond nodes with fuzzy match co', done => { + request.get(`${url}/api/gallery?desc=co`) + .set({ Authorization: `Bearer ${this.tempToken}` }) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + console.log('matching notes', res.body.length) + let fuzzyName = fuzzyRegex('co') + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].desc).to.match(fuzzyName) + } + done() + }) + }) + }) + }) + + describe('testing GET /api/public/gallery', function(){ + describe('with valid request', function(){ + let options = { + users: 4, + gallerys: 3, + pics: 4, + } + + before( done => mockManyEverything.call(this, options, done)) + it('should respond nodes with fuzzy match co', done => { + request.get(`${url}/api/public/gallery`) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + done() + }) + }) + }) + + describe('with ?username=lu', function(){ + let options = { + users: 30, + gallerys: 1, + pics: 1, + } + + before( done => mockManyEverything.call(this, options, done)) + it('should respond nodes with fuzzy match lu', done => { + request.get(`${url}/api/public/gallery?username=lu`) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + let fuzzy = fuzzyRegex('lu') + console.log('matches found', res.body.length) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].username).to.match(fuzzy) + } + done() + }) + }) + }) + + describe('with ?name=lu', function(){ + let options = { + users: 5, + gallerys: 10, + pics: 1, + } + + before( done => mockManyEverything.call(this, options, done)) + it('should respond nodes with fuzzy match lu', done => { + request.get(`${url}/api/public/gallery?name=lu`) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + let fuzzy = fuzzyRegex('lu') + console.log('matches found', res.body.length) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].name).to.match(fuzzy) + } + done() + }) + }) + }) + + describe('with ?itemcount=4', function(){ + let options = { + users: 2, + gallerys: 5, + pics: 10, + } + + before( done => mockManyEverything.call(this, options, done)) + it('each galery should have 4 pics', done => { + request.get(`${url}/api/public/gallery?itemcount=4`) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].pics.length).to.equal(4) + } + done() + }) + }) + }) + + describe('with ?pagesize=4', function(){ + let options = { + users: 2, + gallerys: 5, + pics: 10, + } + + before( done => mockManyEverything.call(this, options, done)) + it('show top 4 galerys with 10 pics', done => { + request.get(`${url}/api/public/gallery?pagesize=4`) + .end((err, res) => { + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(4) + for (let i=0; i < res.body.length; i++){ + expect(res.body[i].pics.length).to.equal(10) + } + done() + }) + }) + }) + }) +}) diff --git a/lab-raziyeh/test/lib/aws-mocks.js b/lab-raziyeh/test/lib/aws-mocks.js new file mode 100644 index 0000000..5e8db65 --- /dev/null +++ b/lab-raziyeh/test/lib/aws-mocks.js @@ -0,0 +1,41 @@ +'use strict' + +const AWSMock = require('aws-sdk-mock') + +module.exports = exports = {} + +exports.uploadMock = { + ETag: '"5eefd06b5b384cc52f35a0c49414ea31"', + VersionId: '_F6K4BKgNMBKeTvrPEd27rl_4qPQl9Hy', + Location: 'https://slugram-assets.s3.amazonaws.com/cd52be0bc15dd9500e87d9afcf35c19e.png', + key: 'cd52be0bc15dd9500e87d9afcf35c19e.png', + Key: 'cd52be0bc15dd9500e87d9afcf35c19e.png', + Bucket: 'slugram-assets', +} + +AWSMock.mock('S3', 'upload', function(params, callback){ + if(params.ACL !== 'public-read') + return callback(new Error('ACL must be public read')) + if(params.Bucket !== process.env.AWS_BUCKET) + return callback(new Error('Bucket must be slugram-assets')) + if(!params.Key) + return callback(new Error('requres Key')) + if(!params.Body) + return callback(new Error('requires body')) + callback(null, exports.uploadMock) +}) + +exports.deleteMock = { + DeleteMarker: 'true', + VersionId: 'lv9XPH0r.UfGZERuv3u7WwxkIzwPKP2d', +} + +AWSMock.mock('S3', 'deleteObject', function(params, callback){ + if(params.Bucket !== process.env.AWS_BUCKET) + return callback(new Error('Bucket must be slugram-assets')) + if(!params.Key) + return callback(new Error('requres Key')) + callback(null, exports.deleteMock) +}) + + diff --git a/lab-raziyeh/test/lib/clean-db.js b/lab-raziyeh/test/lib/clean-db.js new file mode 100644 index 0000000..04e0a33 --- /dev/null +++ b/lab-raziyeh/test/lib/clean-db.js @@ -0,0 +1,18 @@ +'use strict' + +const debug = require('debug')('slugram:clean-db') + +const Pic = require('../../model/pic.js') +const User = require('../../model/user.js') +const Gallery = require('../../model/gallery.js') + +module.exports = function(done){ + debug('clean up database') + Promise.all([ + Pic.remove({}), + User.remove({}), + Gallery.remove({}), + ]) + .then( () => done()) + .catch(done) +} diff --git a/lab-raziyeh/test/lib/everything-mock.js b/lab-raziyeh/test/lib/everything-mock.js new file mode 100644 index 0000000..20e607f --- /dev/null +++ b/lab-raziyeh/test/lib/everything-mock.js @@ -0,0 +1,122 @@ +'use strict' + +const Promise = require('bluebird') +const lorem = require('lorem-ipsum') +const debug = require('debug')('slugram:gallery-mock-everything') + +const Pic = require('../../model/pic.js') +const User = require('../../model/user.js') +const Gallery = require('../../model/gallery.js') + +module.exports = function(options, done){ + debug('mocking users, gallerys, and pics') + if(!checkOptions) + return done('bad options') + + // make usercount + // make gallerycount for each user + // make pictures for each gallery + this.tempUserData = [] + this.tempGallerys = [] + this.tempPics = [] + + let makeUsers = [] + for(var i=0; i { + this.tempUserData.push(userdata) + let makeUserGallerys = [] + let userID = userdata.tempUser._id.toString() + let username = userdata.tempUser.username + for(var i=0; i { + return Promise.resolve(userGallerys) + .map(gallery => { + let makeGalleryPics = [] + let userID = gallery.userID.toString() + let username = gallery.username + for(var i=0; i { + this.tempPics.push(pic) + let picID = pic._id.toString() + gallery.pics.push(picID) + return gallery.save() + }) + .each(gallery => this.tempGallerys.push(gallery)) + }) + }) + .then(() => done()) + .catch(done) +} + +function checkOptions(options){ + if (!options.users) + return false + if (!options.gallerys) + return false + if (!options.pics) + return false + return true +} + +function mockAUser(){ + let username = lorem({count: 4, units: 'word'}).split(' ').join('-') + let password = lorem({count: 4, units: 'word'}).split(' ').join('-') + let email= lorem({count: 4, units: 'word'}).split(' ').join('-') + let exampleUser = { + username, + password, + email: `${email}@slug.slug`, + } + let tempPassword = password + let tempUser, tempToken + return new User(exampleUser) + .generatePasswordHash(tempPassword) + .then( user => { + tempUser = user + return user.generateToken() + }) + .then( token => { + tempToken = token + return { + tempUser, + tempToken, + tempPassword, + } + }) +} + +function mockAGallery(userID, username){ + let name = lorem({count: 2, units: 'word'}) + let desc = lorem({count: 2, units: 'sentence'}) + let exampleGallery = { name, desc , userID, username} + return new Gallery(exampleGallery).save() +} + +function mockAPic(userID, username){ + let name = lorem({count: 2, units: 'word'}) + let desc = lorem({count: 2, units: 'sentence'}) + let uri = lorem({count: 5, units: 'word'}).split(' ').join('-') + let objectKey = lorem({count: 5, units: 'word'}).split(' ').join('') + let imageURI = `https://${uri}/${objectKey}` + let examplePicData = { + name, + desc, + userID, + username, + imageURI, + objectKey, + created: new Date(), + } + return new Pic(examplePicData).save() +} diff --git a/lab-raziyeh/test/lib/gallery-mock.js b/lab-raziyeh/test/lib/gallery-mock.js new file mode 100644 index 0000000..9202401 --- /dev/null +++ b/lab-raziyeh/test/lib/gallery-mock.js @@ -0,0 +1,26 @@ +'use strict' + +const debug = require('debug')('slugram:gallery-mock') +const userMock = require('./user-mock.js') +const Gallery = require('../../model/gallery.js') + + +module.exports = function(done){ + debug('create mock gallery') + let exampleGallery = { + name: 'beach adventure', + desc: 'not enough sun screan ouch', + } + userMock.call(this, err => { + if (err) + return done(err) + exampleGallery.userID = this.tempUser._id.toString() + exampleGallery.username = this.tempUser.username + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery + done() + }) + .catch(done) + }) +} diff --git a/lab-raziyeh/test/lib/mock-many-gallerys.js b/lab-raziyeh/test/lib/mock-many-gallerys.js new file mode 100644 index 0000000..236945d --- /dev/null +++ b/lab-raziyeh/test/lib/mock-many-gallerys.js @@ -0,0 +1,32 @@ +'use strict' + +const debug = require('debug')('slugram:gallery-mock') +const userMock = require('./user-mock.js') +const Gallery = require('../../model/gallery.js') +const lorem = require('lorem-ipsum') + +module.exports = function(count, done){ + debug(`mock ${count}gallerys`) + userMock.call(this, err => { + if (err) return done(err) + let galleryMocks = [] + let userID = this.tempUser._id.toString() + let username = this.tempUser.username + for(var i=0; i { + this.tempGallerys = tempGallerys + done() + }) + .catch(done) + }) +} + +function mockAGallery(userID, username){ + let name = lorem({count: 2, units: 'word'}) + let desc = lorem({count: 2, units: 'sentence'}) + let exampleGallery = { name, desc , userID, username} + return new Gallery(exampleGallery).save() +} diff --git a/lab-raziyeh/test/lib/mock-many-pics.js b/lab-raziyeh/test/lib/mock-many-pics.js new file mode 100644 index 0000000..4d380c3 --- /dev/null +++ b/lab-raziyeh/test/lib/mock-many-pics.js @@ -0,0 +1,54 @@ + +'use strict' + +const Pic = require('../../model/pic.js') +const debug = require('debug')('slugram:gallery-mock') +const galleryMock = require('./gallery-mock.js') +const lorem = require('lorem-ipsum') + +// create a uesr, token, pass, gallery +// create a bunch of pics +// add pic ids to gallery +// save gallery +// done +module.exports = function(count, done){ + debug(`mock ${count}gallerys`) + galleryMock.call(this, err => { + if (err) return done(err) + let picMocks = [] + let userID = this.tempUser._id.toString() + let username = this.tempUser.username + for(var i=0; i { + tempPics.forEach(pic => { + let picID = pic._id.toString() + this.tempGallery.pics.push(picID) + }) + this.tempPics = tempPics + return this.tempGallery.save() + }) + .then(() => done()) + .catch(done) + }) +} + +function mockAPic(userID, username){ + let name = lorem({count: 2, units: 'word'}) + let desc = lorem({count: 2, units: 'sentence'}) + let uri = lorem({count: 5, units: 'word'}).split(' ').join('-') + let objectKey = lorem({count: 5, units: 'word'}).split(' ').join('') + let imageURI = `https://${uri}/${objectKey}` + let examplePicData = { + name, + desc, + userID, + username, + imageURI, + objectKey, + created: new Date(), + } + return new Pic(examplePicData).save() +} diff --git a/lab-raziyeh/test/lib/mock-many-users.js b/lab-raziyeh/test/lib/mock-many-users.js new file mode 100644 index 0000000..198bb41 --- /dev/null +++ b/lab-raziyeh/test/lib/mock-many-users.js @@ -0,0 +1,50 @@ +'use strict' + +const debug = require('debug')('sulgram:mock-many-users') +const User = require('../../model/user.js') +const lorem = require('lorem-ipsum') + +module.exports = function(count, done){ + debug(`creating ${count} users`) + let userMocks = [] + + for(var i=0; i { + this.tempUsers = tempUsers + done() + }) + .catch(done) + +} + +function mockAUser(){ + let username = lorem({count: 2, units: 'word'}).split(' ').join('-') + let password = lorem({count: 2, units: 'word'}).split(' ').join('-') + let email= lorem({count: 2, units: 'word'}).split(' ').join('-') + let exampleUser = { + username, + password, + email: `${email}@slug.slug`, + } + let tempPassword = password + let tempUser, tempToken + return new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then( user => { + tempUser = user + return user.generateToken() + }) + .then( token => { + tempToken = token + return { + tempUser, + tempToken, + tempPassword, + } + }) +} diff --git a/lab-raziyeh/test/lib/pic-mock.js b/lab-raziyeh/test/lib/pic-mock.js new file mode 100644 index 0000000..aa96973 --- /dev/null +++ b/lab-raziyeh/test/lib/pic-mock.js @@ -0,0 +1,36 @@ +'use strict' + +// npm modules +const debug = require('debug')('slugram:pic-mock') + +// app modules +const Pic = require('../../model/pic.js') +const awsMocks = require('./aws-mocks.js') +const galleryMock = require('./gallery-mock.js') +const lorem = require('lorem-ipsum') + +module.exports = function(done){ + debug('creating mock pic') + let name = lorem({count: 2, units: 'word'}) + let desc = lorem({count: 2, units: 'sentence'}) + let examplePicData = { + name, + desc, + created: new Date(), + imageURI: awsMocks.uploadMock.Location, + objectKey: awsMocks.uploadMock.Key, + } + + galleryMock.call(this, err => { + if (err) return done(err) + examplePicData.username = this.tempUser.username + examplePicData.userID = this.tempUser._id.toString() + examplePicData.galleryID = this.tempGallery._id.toString() + new Pic(examplePicData).save() + .then( pic => { + this.tempPic = pic + done() + }) + .catch(done) + }) +} diff --git a/lab-raziyeh/test/lib/server-ctrl.js b/lab-raziyeh/test/lib/server-ctrl.js new file mode 100644 index 0000000..4aaf37f --- /dev/null +++ b/lab-raziyeh/test/lib/server-ctrl.js @@ -0,0 +1,30 @@ +'use strict' + +const debug = require('debug')('slugram:server-ctrl') + +module.exports = exports = {} + +exports.serverUp = function(server, done){ + if (!server.isRunning){ + server.listen(process.env.PORT, () => { + server.isRunning = true + debug('server up') + done() + }) + return + } + done() +} + +exports.serverDown = function(server, done){ + if(server.isRunning){ + server.close(err => { + if (err) return done(err) + server.isRunning = false + debug('server down') + done() + }) + return + } + done() +} diff --git a/lab-raziyeh/test/lib/test-env.js b/lab-raziyeh/test/lib/test-env.js new file mode 100644 index 0000000..fb41f42 --- /dev/null +++ b/lab-raziyeh/test/lib/test-env.js @@ -0,0 +1,8 @@ +'use strict' +process.env.MONGODB_URI = 'mongodb://localhost/slugtesting' +process.env.PORT = 8888 +process.env.NODE_ENV = 'testing' +process.env.APP_SECRET = 'lulwat fake secret for tests' +process.env.AWS_ACCESS_KEY_ID = 'FAKEKEYFORAWS' +process.env.AWS_SECRET_ACCESS_KEY = 'FAKEACESSKEY' +process.env.AWS_BUCKET = 'slugram-assets-testing' diff --git a/lab-raziyeh/test/lib/user-mock.js b/lab-raziyeh/test/lib/user-mock.js new file mode 100644 index 0000000..e4e67a6 --- /dev/null +++ b/lab-raziyeh/test/lib/user-mock.js @@ -0,0 +1,30 @@ +'use strict' + +const debug = require('debug')('sulgram:user-mock') +const User = require('../../model/user.js') +const lorem = require('lorem-ipsum') + +module.exports = function(done){ + debug('create mock user') + let username = lorem({count: 2, units: 'word'}).split(' ').join('-') + let password = lorem({count: 2, units: 'word'}).split(' ').join('-') + let email= lorem({count: 2, units: 'word'}).split(' ').join('-') + let exampleUser = { + username, + password, + email: `${email}@slug.slug`, + } + this.tempPassword = password + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then( user => { + this.tempUser = user + return user.generateToken() + }) + .then( token => { + this.tempToken = token + done() + }) + .catch(done) +} diff --git a/lab-raziyeh/test/pic-router-test.js b/lab-raziyeh/test/pic-router-test.js new file mode 100644 index 0000000..b410eb7 --- /dev/null +++ b/lab-raziyeh/test/pic-router-test.js @@ -0,0 +1,425 @@ +'use strict' + +// mock third party services +require('./lib/test-env.js') +const awsMocks = require('./lib/aws-mocks.js') + +// npm modules +const expect = require('chai').expect +const request = require('superagent') + +// app modules + +const picMock = require('./lib/pic-mock.js') +const cleanDB = require('./lib/clean-db.js') +const userMock = require('./lib/user-mock.js') +let fuzzyRegex = require('../lib/fuzzy-regex.js') +const serverCtrl = require('./lib/server-ctrl.js') +const galleryMock = require('./lib/gallery-mock.js') +const mockManyPics = require('./lib/mock-many-pics.js') +const mockManyEverything = require('./lib/everything-mock.js') + +// module constants +const server = require('../server.js') +const url = `http://localhost:${process.env.PORT}` + +const examplePic = { + name: 'sunburn', + desc: 'owie no thank you', + file: `${__dirname}/data/shield.png`, +} + +describe('testing pic-router', function(){ + // start server before all tests + before( done => serverCtrl.serverUp(server, done)) + // stop server before all tests + after(done => serverCtrl.serverDown(server, done)) + // remove all models from database after every test + afterEach(done => cleanDB(done)) + + describe('testing post /api/gallery/:id/pic', function(){ + describe('with valid token and data', function(){ + before(done => galleryMock.call(this, done)) + it('should return a pic', done => { + request.post(`${url}/api/gallery/${this.tempGallery._id}/pic`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .field('name', examplePic.name) + .field('desc', examplePic.desc) + .attach('file', examplePic.file) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(res.body.name).to.equal(examplePic.name) + expect(res.body.desc).to.equal(examplePic.desc) + expect(res.body.imageURI).to.equal(awsMocks.uploadMock.Location) + expect(res.body.objectKey).to.equal(awsMocks.uploadMock.Key) + done() + }) + }) + }) + + describe('with no name', function(){ + before(done => galleryMock.call(this, done)) + it('should respond with status 400', done => { + request.post(`${url}/api/gallery/${this.tempGallery._id}/pic`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .field('desc', examplePic.desc) + .attach('file', examplePic.file) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with no desc', function(){ + before(done => galleryMock.call(this, done)) + it('should respond with status 400', done => { + request.post(`${url}/api/gallery/${this.tempGallery._id}/pic`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .field('name', examplePic.name) + .attach('file', examplePic.file) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with no file', function(){ + before(done => galleryMock.call(this, done)) + it('should respond with status 400', done => { + request.post(`${url}/api/gallery/${this.tempGallery._id}/pic`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .field('desc', examplePic.desc) + .field('name', examplePic.name) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with invalid token', function(){ + before(done => galleryMock.call(this, done)) + it('should respond with status 401', done => { + request.post(`${url}/api/gallery/${this.tempGallery._id}/pic`) + .set({Authorization: `Bearer ${this.tempToken}bad`}) + .field('desc', examplePic.desc) + .field('name', examplePic.name) + .attach('file', examplePic.file) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('with invalid galleryID', function(){ + before(done => galleryMock.call(this, done)) + it('should respond with status 404', done => { + request.post(`${url}/api/gallery/${this.tempGallery._id}bad/pic`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .field('desc', examplePic.desc) + .field('name', examplePic.name) + .attach('file', examplePic.file) + .end((err, res) => { + expect(res.status).to.equal(404) + expect(res.text).to.equal('NotFoundError') + done() + }) + }) + }) + }) + + describe('testing DELETE /api/gallery/:gallryID/pic/:picID', function(){ + describe('with valid token and ids', function(){ + before(done => picMock.call(this, done)) + + it('should respond with status 204', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}/pic/${this.tempPic._id}`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(204) + done() + }) + }) + }) + + describe('with invalid token', function(){ + before(done => picMock.call(this, done)) + it('should respond with status 401', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}/pic/${this.tempPic._id}`) + .set({Authorization: `Bearer ${this.tempToken}bad`}) + .end((err, res) => { + expect(res.status).to.equal(401) + expect(res.text).to.equal('UnauthorizedError') + done() + }) + }) + }) + + describe('should resond with 401', function(){ + before(done => picMock.call(this, done)) + before(done => userMock.call(this, done)) + + it('should respond with status 401', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}/pic/${this.tempPic._id}`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .end((err, res) => { + expect(res.status).to.equal(401) + done() + }) + }) + }) + + describe('no auth header', function(){ + before(done => picMock.call(this, done)) + it('should respond with status 400', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}/pic/${this.tempPic._id}`) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with no bearer auth', function(){ + before(done => picMock.call(this, done)) + it('should respond with status 400', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}/pic/${this.tempPic._id}`) + .set({Authorization: 'lul this is bad'}) + .end((err, res) => { + expect(res.status).to.equal(400) + expect(res.text).to.equal('BadRequestError') + done() + }) + }) + }) + + describe('with invalid galleryID', function(){ + before(done => picMock.call(this, done)) + it('should respond with status 404', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}bad/pic/${this.tempPic._id}`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .end((err, res) => { + expect(res.status).to.equal(404) + expect(res.text).to.equal('NotFoundError') + done() + }) + }) + }) + + describe('with invalid picID', function(){ + before(done => picMock.call(this, done)) + it('should respond with status 404', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}/pic/${this.tempPic._id}bad`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .end((err, res) => { + expect(res.status).to.equal(404) + expect(res.text).to.equal('NotFoundError') + done() + }) + }) + }) + }) + + describe('testing GET /api/public/pic', function(){ + describe('with valid request', function(){ + before(done => mockManyPics.call(this, 100, done)) + it ('should return an array of pics', done => { + request.get(`${url}/api/public/pic`) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + expect(res.body.length).to.equal(50) + for(let i=0; i mockManyPics.call(this, 100, done)) + it ('should return an array of pics', done => { + request.get(`${url}/api/public/pic?name=do`) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + let fuzzy = fuzzyRegex('do') + for(let i=0; i mockManyPics.call(this, 50, done)) + it ('should return an array of pics', done => { + request.get(`${url}/api/public/pic?desc=lorem`) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + let fuzzy = fuzzyRegex('lorem') + for(let i=0; i mockManyPics.call(this, 50, done)) + it ('should return an array of pics', done => { + request.get(`${url}/api/public/pic?desc=lorem%20ip`) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + let fuzzy = fuzzyRegex('lorem ip') + for(let i=0; i mockManyPics.call(this, 100, done)) + it ('should return an array of pics', done => { + request.get(`${url}/api/public/pic?desc=lorem&name=do`) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + let fuzzyName = fuzzyRegex('do') + let fuzzyDesc = fuzzyRegex('lo') + for(let i=0; i mockManyEverything.call(this, options, done)) + //before(function(done){ + //this.timeout(5000) + //mockManyEverything.call(this, 20, function(err){ + //if(err) return done(err) + //done() + //}) + //}) + + it ('should return an array of pics', done => { + request.get(`${url}/api/public/pic?username=lop`) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + let fuzzyuser = fuzzyRegex('lo') + console.log('pics in response', res.body.length) + for(let i=0; i mockManyPics.call(this, 100, done)) + it ('should return an array of pics', done => { + request.get(`${url}/api/pic`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .end((err, res) => { + if (err) + return done(err) + expect(res.status).to.equal(200) + expect(Array.isArray(res.body)).to.equal(true) + for(let i=0; i mockManyPics.call(this, 100, done)) + it ('should return an array of pics', done => { + request.get(`${url}/api/pic`) + .set({Authorization: `Bearer ${this.tempToken}bad`}) + .end((err, res) => { + expect(res.status).to.equal(401) + done() + }) + }) + }) + + + describe('with ?name=do', function(){ + before(done => mockManyPics.call(this, 100, done)) + it ('should return an array of pics', done => { + request.get(`${url}/api/pic?name=do`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .end((err, res) => { + expect(res.status).to.equal(200) + let fuzzyName = fuzzyRegex('do') + for(let i=0; i mockManyPics.call(this, 10, done)) + it ('should return an array of pics', done => { + request.get(`${url}/api/pic?desc=do`) + .set({Authorization: `Bearer ${this.tempToken}`}) + .end((err, res) => { + expect(res.status).to.equal(200) + let fuzzyName = fuzzyRegex('do') + for(let i=0; i { + let params = { + Bucket: process.env.AWS_BUCKET, + Key: 'wat', + Body: 'lul', + ACL: 'public-read', + } + + s3UploadPromise(params) + .then(data => { + let uploadMock = awsMocks.uploadMock + expect(data.ETag).to.equal(uploadMock.ETag) + expect(data.Location).to.equal(uploadMock.Location) + expect(data.Key).to.equal(uploadMock.Key) + done() + }) + .catch(done) + }) + }) + + describe('with no ACL', function(){ + it('should return an error', done => { + let params = { + Bucket: process.env.AWS_BUCKET, + Key: 'wat', + Body: 'lul', + } + + s3UploadPromise(params) + .then(done) + .catch(err => { + expect(err.message).to.equal('ACL must be public read') + done() + }) + }) + }) + + describe('with with no key', function(){ + it('should return an aws response', done => { + let params = { + Bucket: process.env.AWS_BUCKET, + Body: 'lul', + ACL: 'public-read', + } + + s3UploadPromise(params) + .then(done) + .catch(err => { + expect(err.message).to.equal('requres Key') + done() + }) + }) + }) + + describe('with with no body', function(){ + it('should return an aws response', done => { + let params = { + Bucket: process.env.AWS_BUCKET, + Key: 'wat', + ACL: 'public-read', + } + + s3UploadPromise(params) + .then(done) + .catch(err => { + expect(err.message).to.equal('requires body') + done() + }) + }) + }) +}) + + diff --git a/lab-raziyeh/webpack.config.js b/lab-raziyeh/webpack.config.js new file mode 100644 index 0000000..a673069 --- /dev/null +++ b/lab-raziyeh/webpack.config.js @@ -0,0 +1,74 @@ +'use strict'; + +require('dotenv').load({path: `${__dirname}/.client.env`}); +// if (!process.env.API_URL || !process.env.NODE_ENV || !process.env.TITLE){ +// process.exit(1); +// } + +const webpack = require('webpack'); +const HTMLPlugin = require('html-webpack-plugin'); +const CleanPlugin = require('clean-webpack-plugin'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); + +const production = process.env.NODE_ENV === 'production'; + +let plugins = [ + new ExtractTextPlugin('bundle.css'), + new HTMLPlugin({ template: `${__dirname}/app/index.html` }), + new webpack.DefinePlugin({ + __API_URL__: JSON.stringify(process.env.API_URL), + __GOOGLE_CLIENT_ID__: JSON.stringify(process.env.GOOGLE_CLIENT_ID), + __TITLE__: JSON.stringify(process.env.TITLE), + __DEBUG__: JSON.stringify(!production), + }), +]; + +if (production){ + plugins = plugins.concat([ + new webpack.optimize.UglifyJsPlugin({ + mangle: true, + compress: { + warnings: false, + }, + }), + new CleanPlugin(), + ]); +} + +module.exports = { + entry: `${__dirname}/app/entry.js`, + devtool: production ? false : 'eval', + plugins, + output: { + path: 'build', + filename: 'bundle.js', + }, + sassLoader: { + includePaths: [`${__dirname}/app/scss/lib`], + }, + module: { + loaders: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel', + }, + { + test: /\.html$/, + loader: 'html', + }, + { + test: /\.(woff|ttf|svg|eot).*/, + loader: 'url?limit=10000&name=font/[name].[ext]', + }, + { + test: /\.(jpg|jpeg|bmp|tiff|gif|png)$/, + loader: 'url?limit=10000&name=image/[hash].[ext]', + }, + { + test: /\.scss$/, + loader: ExtractTextPlugin.extract('style', 'css!resolve-url!sass?sourceMap'), + }, + ], + }, +};