Skip to content

Services and Factories

The Stratpoint Timesheet Application uses AngularJS services and factories defined directly on the main StratpointTSApp module. All services are implemented in /public/app/js/mine/services.js. This section details the actual service implementation and key services.

Service Architecture

All Services Defined on Main Module

// All services/factories are defined directly on StratpointTSApp in /public/app/js/mine/services.js
StratpointTSApp
.factory('ServiceName', ['dependency1', 'dependency2', function(dependency1, dependency2) {
    // Implementation
}])

HTTP Interceptors

Response Interceptor

Handles authentication token refresh and error responses.

// From /public/app/js/mine/services.js
StratpointTSApp
.factory('responseInterceptors', ["$rootScope", "$location", "$q", "$window", "$cookieStore", "Flash", "indexedDBDataSvc", "Authenticate", "ActiveForm", function ($rootScope, $location, $q, $window, $cookieStore, Flash, indexedDBDataSvc, Authenticate, ActiveForm) {

    var clearSession = function (){
        Authenticate.customClearLocalStorage();
        $cookieStore.remove('timesheet.user');
        $cookieStore.remove('timesheet.permissions');

        $rootScope.loggedPermissionList = [];
        $rootScope.$broadcast('permissionsChanged');

        delete $rootScope.navItems;
        delete $rootScope.currentUser;
        delete $rootScope.currentToken;
    };

    var checkNewAuth = function(response){
        if(response.headers("content-type") == 'application/json'){
            if(typeof response.data.body.newAuth != 'undefined'){
                var newAuthData = response.data.body.newAuth;
                $rootScope.userId = newAuthData.user.id;
                var perms = newAuthData.user.permission_ids.split(',');
                Authenticate.setUser(newAuthData, true);
                Authenticate.setPermissions(perms, true);
            }
        }
    }

    return {
        response: function(response){
            checkNewAuth(response);
            return response;
        },
        responseError: function(response){
            checkNewAuth(response);

            if(response.status == 429) { // too many request
                Flash.show(response.data.message, 'error');
            } else if(response.status == 503) {
                $window.location.reload();
            } else if(response.status == 504) { // cloudfront timeout
                Flash.show("Server Timeout error was encountered. Please report to timesheet@stratpoint.com");
            } else if (response.status == 403 || response.status == 401 || response.status == 406) {
                if(response.status == 401){
                    Flash.show("Authentication error was encountered. You will be redirected to the login page.", 'error', 3000);
                } else if (response.status == 403){
                    Flash.show("Access not allowed", 'error');
                } else if (response.status == 406){
                    Flash.show("Invalid request", 'error');
                }
                clearSession();
                $location.path('/login');
            }
            return $q.reject(response);
        }
    };
}])

Request Interceptor

Adds authentication headers to outgoing requests.

// From /public/app/js/mine/services.js
StratpointTSApp
.factory('requestInterceptors', ["$rootScope", "$window", function ($rootScope, $window) {
    return {
        request: function (config) {
            config.headers = config.headers || {};

            // Add authentication token if available
            if ($rootScope.currentToken) {
                config.headers.Authorization = 'Bearer ' + $rootScope.currentToken;
            }

            return config;
        }
    };
}])

Authentication Services

Authenticate Service

Core authentication service handling user login, logout, and permission management.

// From /public/app/js/mine/services.js
StratpointTSApp
.factory('Authenticate', ['$cookieStore', '$rootScope', '$location', '$q', 'Flash', 'BASE_URL', 'ActiveForm', function($cookieStore, $rootScope, $location, $q, Flash, BASE_URL, ActiveForm){

    return {
        setUser: function(userData, remember) {
            if (remember) {
                localStorage['timesheet.user'] = JSON.stringify(userData);
            } else {
                $cookieStore.put('timesheet.user', userData);
            }

            $rootScope.currentUser = userData.user;
            $rootScope.currentToken = userData.access_token;
            $rootScope.userId = userData.user.id;
        },

        getUser: function() {
            var user = localStorage['timesheet.user'] || $cookieStore.get('timesheet.user');
            return user ? (typeof user === 'string' ? JSON.parse(user) : user) : null;
        },

        hasPermission: function(permissionId) {
            var permissions = $rootScope.loggedPermissionList || [];
            return permissions.indexOf(permissionId.toString()) !== -1;
        },

        setPermissions: function(permissions, remember) {
            if (remember) {
                localStorage['timesheet.permissions'] = JSON.stringify(permissions);
            } else {
                $cookieStore.put('timesheet.permissions', permissions);
            }
            $rootScope.loggedPermissionList = permissions;
        },

        customClearLocalStorage: function() {
            delete localStorage['timesheet.user'];
            delete localStorage['timesheet.permissions'];
        },

        logout: function() {
            this.customClearLocalStorage();
            $cookieStore.remove('timesheet.user');
            $cookieStore.remove('timesheet.permissions');

            $rootScope.loggedPermissionList = [];
            delete $rootScope.currentUser;
            delete $rootScope.currentToken;
            delete $rootScope.navItems;

            $location.path('/login');
        }
    };
}])

AuthRequest Service

Handles authentication API requests.

// From /public/app/js/mine/services.js
StratpointTSApp
.factory('AuthRequest', ['$resource', '$http', '$rootScope', '$location', '$q', 'BASE_URL', 'BasicData', 'Flash', 'Authenticate', function($resource, $http, $rootScope, $location, $q, BASE_URL, BasicData, Flash, Authenticate){

    return $resource(BASE_URL + "/api/v2/authenticate/:id", {id: '@id'}, {
        login: {
            method: 'POST'
        },
        logout: {
            method: 'DELETE'
        },
        refreshToken: {
            method: 'PATCH'
        }
    });
}])

Data Services

BasicData Service

Core data service for CRUD operations using $resource.

// From /public/app/js/mine/services.js
StratpointTSApp
.factory('BasicData', ['$http','$resource', '$q', '$filter', '$timeout', 'BASE_URL', 'Cache', function($http, $resource, $q, $filter, $timeout, BASE_URL, Cache){

    var rs = $resource(BASE_URL + "/api/v2/:backendRoute/:id", {id: '@id', backendRoute: '@backendRoute' } , {
        update: { method: 'PUT' },
        Add: { method: 'POST' },
        Edit: { method: 'PUT' }
    });

    rs.getFilterDefer = function (backendRoute) {
        var deferred = $q.defer();
        var cacheName = backendRoute + '.filters';

        if(typeof ( result = Cache.get(cacheName)) == 'undefined' ) {
            $http.get(BASE_URL + "/api/v2/" + backendRoute+ "/getfilters").then( function(response){
                if(response.data.success){
                    var result = response.data.data;
                    Cache.put(cacheName, result);
                    deferred.resolve(angular.copy(result));
                } else {
                    deferred.reject();
                }
            });
        } else {
            deferred.resolve(angular.copy(result));
        }
        return deferred;
    };

    rs.makePost = function(data) {
        return $http.post(BASE_URL + "/api/v2/" + data.backendRoute, data );
    };

    rs.makePut = function(data) {
        return $http.put(BASE_URL + "/api/v2/" + data.backendRoute, data );
    };

    rs.makeDelete = function(data) {
        return $http.delete(BASE_URL + "/api/v2/" + data.backendRoute + '/' + data.id, data );
    };

    rs.makeGet = function(url, data) {
        var deferred = $q.defer();
        var cacheName = url + JSON.stringify(data || {});

        if(typeof (result = Cache.get(cacheName)) == 'undefined') {
            $http.get(BASE_URL + "/api/v2/" + url, {params: data}).then(function(response){
                Cache.put(cacheName, response.data);
                deferred.resolve(response.data);
            }, function(error) {
                deferred.reject(error);
            });
        } else {
            deferred.resolve(angular.copy(result));
        }
        return deferred.promise;
    };

    return rs;
}])

Specialized Data Services

TimelogData Service

// From /public/app/js/mine/services.js
StratpointTSApp
.factory("TimelogData", ["BasicData", "Cache", "$q", "$filter", function(BasicData, Cache, $q, $filter){
    return {
        getTimelogs: function(userId, startDate, endDate) {
            return BasicData.makeGet('timelogs/' + userId + '/' + startDate + '/' + endDate);
        },

        saveTimelog: function(timelogData) {
            timelogData.backendRoute = 'timelogs';
            return timelogData.id ? BasicData.makePut(timelogData) : BasicData.makePost(timelogData);
        },

        deleteTimelog: function(timelogId) {
            return BasicData.makeDelete({
                backendRoute: 'timelogs',
                id: timelogId
            });
        }
    };
}])

UserData Service

// From /public/app/js/mine/services.js
StratpointTSApp
.factory("UserData", ["BasicData", "Cache", "$q", "$filter", function(BasicData, Cache, $q, $filter){
    return {
        getUsers: function(filters) {
            return BasicData.makeGet('users', filters);
        },

        getUserById: function(userId) {
            return BasicData.makeGet('users/' + userId);
        },

        getFilters: function() {
            return BasicData.getFilterDefer('users');
        }
    };
}])

ProjectData Service

// From /public/app/js/mine/services.js
StratpointTSApp
.factory("ProjectData", ["BasicData", "Cache", "Flash", "$q", "$filter", function(BasicData, Cache, Flash, $q, $filter){
    return {
        getProjects: function(filters) {
            return BasicData.makeGet('projects', filters);
        },

        getProjectById: function(projectId) {
            return BasicData.makeGet('projects/' + projectId);
        },

        saveProject: function(projectData) {
            projectData.backendRoute = 'projects';
            return projectData.id ? BasicData.makePut(projectData) : BasicData.makePost(projectData);
        }
    };
}])

Utility Services

Flash Message Service

// From /public/app/js/mine/services.js
StratpointTSApp
.factory('Flash', ['$rootScope','$timeout', function($rootScope, $timeout){
    return {
        show: function(message, type, timeout) {
            type = type || 'info';
            timeout = timeout || 5000;

            $rootScope.flashMessage = {
                message: message,
                type: type,
                show: true
            };

            $timeout(function() {
                $rootScope.flashMessage.show = false;
            }, timeout);
        },

        hide: function() {
            if ($rootScope.flashMessage) {
                $rootScope.flashMessage.show = false;
            }
        }
    };
}])

Cache Service

Client-side caching with IndexedDB fallback.

// From /public/app/js/mine/services.js
StratpointTSApp
.factory('Cache', ['$http', '$q', '$filter', '$rootScope', '$timeout', 'indexedDBDataSvc', 'BASE_URL', 'CACHE_TIMEOUT', 'Flash', 'Authenticate', function($http, $q, $filter, $rootScope, $timeout, indexedDBDataSvc, BASE_URL, CACHE_TIMEOUT, Flash, Authenticate){

    var memoryCache = {};

    return {
        put: function(key, value, timeout) {
            timeout = timeout || CACHE_TIMEOUT;

            memoryCache[key] = {
                value: value,
                timestamp: Date.now(),
                timeout: timeout * 1000
            };

            // Also store in IndexedDB for persistence
            indexedDBDataSvc.setData(key, value);
        },

        get: function(key) {
            var cached = memoryCache[key];

            if (cached) {
                var age = Date.now() - cached.timestamp;
                if (age < cached.timeout) {
                    return cached.value;
                } else {
                    delete memoryCache[key];
                }
            }

            // Try IndexedDB
            return indexedDBDataSvc.getData(key);
        },

        clear: function(pattern) {
            if (pattern) {
                Object.keys(memoryCache).forEach(function(key) {
                    if (key.indexOf(pattern) !== -1) {
                        delete memoryCache[key];
                    }
                });
            } else {
                memoryCache = {};
            }

            indexedDBDataSvc.clearData(pattern);
        }
    };
}])

Data Export Service

// From /public/app/js/mine/services.js
StratpointTSApp
.factory('ExportDataToCsv', function(){
    return {
        exportToCsv: function(data, filename) {
            if (!data || !data.length) {
                return;
            }

            var csvContent = "";
            var headers = Object.keys(data[0]);

            // Add headers
            csvContent += headers.join(",") + "\n";

            // Add data rows
            data.forEach(function(row) {
                var values = headers.map(function(header) {
                    var value = row[header] || '';
                    // Escape commas and quotes
                    if (value.toString().indexOf(',') !== -1 || value.toString().indexOf('"') !== -1) {
                        value = '"' + value.toString().replace(/"/g, '""') + '"';
                    }
                    return value;
                });
                csvContent += values.join(",") + "\n";
            });

            // Download file
            var blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
            var link = document.createElement("a");

            if (link.download !== undefined) {
                var url = URL.createObjectURL(blob);
                link.setAttribute("href", url);
                link.setAttribute("download", filename || 'export.csv');
                link.style.visibility = 'hidden';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            }
        }
    };
})

Encryption Service

// From /public/app/js/mine/services.js
StratpointTSApp
.factory("EncryptData", function(){
    return {
        encrypt: function(data, key) {
            // Simple encryption implementation
            // Note: This is for basic obfuscation, not secure encryption
            var encrypted = "";
            for (var i = 0; i < data.length; i++) {
                encrypted += String.fromCharCode(data.charCodeAt(i) ^ key.charCodeAt(i % key.length));
            }
            return btoa(encrypted);
        },

        decrypt: function(encryptedData, key) {
            try {
                var data = atob(encryptedData);
                var decrypted = "";
                for (var i = 0; i < data.length; i++) {
                    decrypted += String.fromCharCode(data.charCodeAt(i) ^ key.charCodeAt(i % key.length));
                }
                return decrypted;
            } catch (e) {
                return null;
            }
        }
    };
})

Summary

The service layer in the Stratpoint Timesheet Application provides:

  1. HTTP Interceptors: Authentication and error handling
  2. Authentication Services: User login, logout, and permission management
  3. Data Services: CRUD operations with caching support
  4. Utility Services: Flash messages, caching, data export, encryption

Key Characteristics: - All services defined directly on StratpointTSApp module - Located in single file: /public/app/js/mine/services.js - Extensive use of $resource for RESTful API communication - Client-side caching with IndexedDB persistence - Integrated authentication and permission management - Support for data export and basic encryption