Source: fedramp.services/csv.service.js

(function(){
    'use strict';

    angular
        .module('fedramp.services')
        .service('CsvService', CsvService);

    CsvService.$inject = ['$log', 'fedrampData', 'helperService'];

    /**
     * @constructor
     * @memberof Services
     */
    function CsvService ($log, fedrampData, helperService) {
        var self = this;

        self.raw = fedrampData.all();

        /**
         * Takes an object and converts to a csv string
         *
         * @public
         * @memberof Service.CsvService
         *
         * @returns
         * A csv string representation of an object.
         */
        self.toCsv = function (data, config) {
            return Papa.unparse(data, config);
        };

        /**
         * Iterates through an array creating an array of flattened objects
         *
         * @public
         * @memberof Services.CsvService
         *
         * @param {array} data
         *  An array of complex objects
         *
         * @returns
         *  A flatten array of array values
         */
        self.flatten = function (data) {
            let rows = [];

            // {
            //     CloudServiceProvider: '',
            //     CloudServiceOffering: '',
            //     Type: '',
            //     LeveragedATO: '',
            //     AuthorizationDate: '',
            //     Agency: '',
            //     Subagency: '',
            //     IndependentAssessor: ''
            // }

            data.forEach(x => {
                switch (x.type) {
                case 'product':
                    rows = rows.concat(productFilter(x.pkgId));
                    break;

                case 'agency':
                    rows = rows.concat(agencyFilter(x.name));
                    break;

                case 'assessor':
                    rows = rows.concat(assessorFilter(x.name));
                    break;
                }
            });

            return rows;
        };

        function productFilter (pkgId) {
            let rows = [];

            let products = self.raw.filter(x => x.pkgId === pkgId);
            if (products && products.length !== 0) {
                let p = products[0];

                // Populate the parent
                rows.push(flattenObject({
                    CloudServiceProvider: p.name,
                    CloudServiceOffering: p.pkg,
                    Type: p.path,
                    LeveragedATO: 'No',
                    AuthorizationDate: helperService.toDate(p.authorizationDate),
                    Agency: p.sponsoringAgency,
                    Subagency: '',
                    IndependentAssessor: p.independentAssessor
                }));

                // Populate any leveraged ATOs
                p.atoLetters.forEach(x => {
                    rows.push(flattenObject({
                        CloudServiceProvider: p.name,
                        CloudServiceOffering: p.pkg,
                        Type: p.path,
                        LeveragedATO: 'Yes',
                        AuthorizationDate: helperService.toDate(x.compliantDate),
                        Agency: x.authorizingAgency,
                        Subagency: x.authorizingSubagency,
                        IndependentAssessor: x.independentAssessor
                    }));
                });
            }

            return rows;
        }

        function agencyFilter (name) {
            let rows = [];

            self.raw.forEach(p => {
                if (p.sponsoringAgency.trim() === name) {
                    // Populate the parent
                    rows.push(flattenObject({
                        CloudServiceProvider: p.name,
                        CloudServiceOffering: p.pkg,
                        Type: p.path,
                        LeveragedATO: 'No',
                        AuthorizationDate: helperService.toDate(p.authorizationDate),
                        Agency: p.sponsoringAgency,
                        Subagency: '',
                        IndependentAssessor: p.independentAssessor
                    }));
                }

                // Populate any leveraged ATOs
                p.atoLetters.forEach(x => {
                    if (x.authorizingAgency.trim() === name || x.authorizingSubagency.trim() === name) {
                        rows.push(flattenObject({
                            CloudServiceProvider: p.name,
                            CloudServiceOffering: p.pkg,
                            Type: p.path,
                            LeveragedATO: 'Yes',
                            AuthorizationDate: helperService.toDate(x.compliantDate),
                            Agency: x.authorizingAgency,
                            Subagency: x.authorizingSubagency,
                            IndependentAssessor: x.independentAssessor
                        }));
                    }
                });
            });

            return rows;
        }

        function assessorFilter (name) {
            let rows = [];

            self.raw.forEach(p => {
                if (p.independentAssessor.trim() === name) {
                    // Populate the parent
                    rows.push(flattenObject({
                        CloudServiceProvider: p.name,
                        CloudServiceOffering: p.pkg,
                        Type: p.path,
                        LeveragedATO: 'No',
                        AuthorizationDate: helperService.toDate(p.authorizationDate),
                        Agency: p.sponsoringAgency,
                        Subagency: '',
                        IndependentAssessor: p.independentAssessor
                    }));
                }

                // Populate any leveraged ATOs
                p.atoLetters.forEach(x => {
                    if (x.independentAssessor.trim() === name) {
                        rows.push(flattenObject({
                            CloudServiceProvider: p.name,
                            CloudServiceOffering: p.pkg,
                            Type: p.path,
                            LeveragedATO: 'Yes',
                            AuthorizationDate: helperService.toDate(x.compliantDate),
                            Agency: x.authorizingAgency,
                            Subagency: x.authorizingSubagency,
                            IndependentAssessor: x.independentAssessor
                        }));
                    }
                });
            });

            return rows;
        }

        /**
         * Iterates through the properties of an object creating a flat structure
         *
         * @public
         * @memberof Services.CsvService
         *
         * @param {object} obj
         *
         * @returns
         *  A flattened object
         */
        function flattenObject (obj) {
            let flat = {};

            for (let prop in obj) {
                if (typeof obj[prop] === 'function') {
                    continue;
                } else if (Array.isArray(obj[prop])) {
                    if (obj[prop].length === 0) {
                        flat[unicornString(prop)] = '';
                        continue;
                    }
                    
                    if (typeof obj[prop][0] === 'string') {
                        flat[unicornString(prop)] = obj[prop].join(', ');
                    } else if (typeof obj[prop][0] === 'object') {
                        let a = flatten(obj[prop]);
                        for (let i = 0; i < a.length; i++) {
                            let o = a[i];
                            for (let p in o) {
                                flat[unicornString(prop) + ' - ' + unicornString(p)] = o[p];
                            }
                        }
                    }
                } else if (typeof obj[prop] === 'object') {
                    let o = flattenObject(obj[prop]);

                    for (let p in o) {
                        flat[unicornString(prop) + ' - ' + unicornString(p)] = o[p];
                    }
                } else {
                    flat[unicornString(prop)] = obj[prop];
                }
            }

            return flat;
        }

        /**
         * Creates a magical and readable string from camelcase
         *
         * @public
         * @memberof Services.CsvService
         *
         * @param {string} camelCase
         *
         * @returns
         *  A human readable string
         */
        function unicornString (camelCase) {
            return camelCase
                .replace(/([A-Z])/g, ' $1')
                .replace(/^./, function (s) { return s.toUpperCase(); });
        }
    }
})();