Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagejs
themeDJangoConfluence
titleProject Structure
├── e2e
├── src
│   ├── app   
│   │   ├── core     
│   │   │   ├── models
│   │   │   └── services 
│   │   ├── shared
│   │   │   ├── components                  # container of all general components 
│   │   │   └── utils                       # container of all general functions 
│   │   ├── views                           # container of all business pages
│   │   │   ├── alarm 
│   │   │   └── ......                      
│   │   ├── app-routing.module.ts                                             
│   │   ├── app.component.css                                            
│   │   ├── app.component.less                                              
│   │   ├── app.component.html                                                                                         
│   │   ├── app.component.ts                                                                                                  
│   ├── assets 
│   │   ├── json                            # container of mock data assets     
│   │   ├── i18n                            # container of internationalization assets                      
│   │   └── images   
│   ├── environments 
│   ├── favicon.ico                        
│   ├── index.html    
│   ├── style.css  
│   ├── style.less                         
│   ├── my-theme.css 
│   ├── my-theme.less  
│   ├── main.ts  
│   ├── polyfill.ts   
│   ├── test.ts   
│   ├── tsconfig.app.json 
│   ├── tsconfig.spec.json 
│   ├── typing.d.ts 
├── .angular-cli.json
├── CHANGELOG.md                            # recorder of all the important changes 
├── karma.conf.js 
├── localproxy.conf.json                    # config for mock server proxy 
├── proxy.conf.json                         # config for server proxy 
├── tsconfig.json
├── package.json
└── README.md   

...

Code Block
languagejs
themeDJangoConfluence
//home.service.ts ——————local codes
…………

  baseUrl = "./assets/json/"
  url = {
    home_serviceData: this.baseUrl + "/home_serviceData.json",
    home_alarmData: this.baseUrl + "/home_alarmData.json",
    home_alarmChartData: this.baseUrl + "/home_alarmChartData.json",
    home_servicebarData:this.baseUrl + "/home_servicebar.json",
    sourceNames: this.baseUrl + "/SourceName.json",
    listSortMasters:this.baseUrl+"/listSortMsters.json",
  }


//home.service.ts ——————online codes
…………
  url = {
    home_serviceData: this.baseUrl + "/uui-lcm/serviceNumByCustomer",
    home_alarmData: this.baseUrl + "/alarm/statusCount",
    home_alarmChartData: this.baseUrl + "/alarm/diagram",
    home_servicebarnsData: this.baseUrl + "/uui-lcm/ns-packages",
    sourceNames: this.baseUrl + "/alarm/getSourceNames",
    listSortMasters: this.baseUrl + "/listSortMasters",
  }

...

Since json-server provides basic service to the mock server, let’s setup first.

Code Block
languagejsbash
themeDJangoConfluence
npm install -g json-server

...

Code Block
DJango
languagejs
themeConfluence
titlepackage.json
"mockproxy": "ng serve --proxy-config localproxy.conf.json”, // switch local server to mock server
"mockconfig": "node ./src/app/mock/server.js --port 3002”,  // start local mock server
"mock": "npm run mockconfig | npm run mockproxy",

...

Code Block
DJango
languagejs
themeConfluence
titleserver.js
const jsonServer = require('json-server');
const server = jsonServer.create();
const middlewares = jsonServer.defaults();
const customersRouters = require('./routes');
const baseUrl = "/usecaseui-server/v1”;

// Set default middlewares
server.use(middlewares);

// Get mock data
const fs = require('fs');
const path = require('path');

let localJsonDb = {};  //import mock datas
const mockFolder = './src/app/mock/json'; //mock json path folder
const filePath = path.resolve(mockFolder);

fileDisplay(filePath);

function fileDisplay(filePath) {
    let fileList = [];
    // Return filelist on based of filePath
    const files = fs.readdirSync(filePath);
    files.forEach((filename) => {
        // Get filename's absolute path
        let filedir = path.join(filePath, filename);
        // Get the file information according to the file path and return an fs.Stats object
        fs.stat(filedir, (err, stats) => {
            if (err) {
                console.warn('Get files failed......');
            } else {
                let isFile = stats.isFile(); // files
                let isDir = stats.isDirectory(); //files folder
                if (isFile) {
                    fileList.push(path.basename(filedir, '.json'));
                    fileList.forEach(item => {
                        localJsonDb[item] = getjsonContent(item);
                    })
                }
                if (isDir) {
                    fileDisplay(filedir);
                }
                Object.keys(fakeoriginalData).map(item => {
                    localJsonDb[item] = fakeoriginalData[item];
                })
            }
        })
    })
    setTimeout(() => {
        runServer(localJsonDb);
    }, 100)
}

function getjsonContent(path) {
    let newpath = `./src/app/mock/json/${path}.json`;
    let result = JSON.parse(fs.readFileSync(newpath));
    return result;
}

function serverRewrite() {
    server.use(jsonServer.rewriter(customersRouters))
}

function runServer(db) {
    server.use(jsonServer.router(db));
}

server.listen(3002, () => {
    console.log('Mock Server is successfully running on port 3002!')
});

...

Code Block
languagejs
themeDJangoConfluence
………    
    let fileList = [];
    // Return filelist on based of filePath
    const files = fs.readdirSync(filePath);
    files.forEach((filename) => {
        // Get filename's absolute path
        let filedir = path.join(filePath, filename);
        // Get the file information according to the file path and return an fs.Stats object
        fs.stat(filedir, (err, stats) => {
            if (err) {
                console.warn('Get files failed......');
            } else {
                let isFile = stats.isFile(); // files
                let isDir = stats.isDirectory(); //files folder
                if (isFile) {
                    fileList.push(path.basename(filedir, '.json'));
                    fileList.forEach(item => {
                        localJsonDb[item] = getjsonContent(item);
                    })
                }
                if (isDir) {
                    fileDisplay(filedir);
                }
                Object.keys(fakeoriginalData).map(item => {
                    localJsonDb[item] = fakeoriginalData[item];
                })
            }
        })
    })
………
function getjsonContent(path) {
    let newpath = `./src/app/mock/json/${path}.json`;
    let result = JSON.parse(fs.readFileSync(newpath));
    return result;
}
………

...

Code Block
languagejs
themeDJangoConfluence
{  
………
  'uui-sotn_getPinterfaceByVpnId': { 'vpn-binding': [ [Object] ] },
  'uui-sotn_getPnfInfo': {
    'pnf-name': 'pnf1000',
    'pnf-id': '79',
    'in-maint': true,
    'resource-version': '195',
    'admin-status': 'up',
    'operational-status': 'up',
    'relationship-list': { relationship: [Array] }
  },
  'uui-sotn_getSpecificLogicalLink': {
    'link-name': 'nodeId-79-ltpId-4_nodeId-78-ltpId-4',
    'in-maint': false,
    'link-type': 'some type',
    'speed-value': 'some speed',
    'resource-version': '13031',
    'operational-status': 'up',
    'relationship-list': { relationship: [Array] }
  },
  xuran_test_data: {
    'esr-system-info-id': 'xuran',
    'service-url': 'http://10.10.10.10:8080/',
    'user-name': 'demo',
    'password': 'demo123456!',
    'system-type': 'ONAP',
    'resource-version': '18873'
  }
………
}

...

Code Block
languagejs
themeDJangoConfluence
function runServer(db) {
    server.use(jsonServer.router(db));
}

...

//import
Code Block
languagejs
themeFadeToGrey
Confluence
titleImport routes.js
const customersRouters = require('./routes’);
………
function serverRewrite() {
    server.use(jsonServer.rewriter(customersRouters))
}

...

//configuration in
Code Block
languagejs
themeConfluenceFadeToGrey
titleConfiguration in routes.js
…………
module.exports =
{
   ///////<-------------general interface--------->/////
        "/api/*": "/$1",
        "/*/*": "/$1_$2",
        "/*/*/*": "/$1_$2_$3",
        "/*/*/*/*": "/$1_$2_$3_$4",
        ////////////////////////////////////
}

...

/////////////////
}


Since then, a basic local server has been built. When you tap ‘npm run mock’, most of the APIs can be mocked. Hurray!

...

Let’s install it first.

Code Block
languageactionscript3bash
themeDJangoConfluence
npm install faker


Then, let’s create a /mock/mock folder to manage the fake data generated by faker.js. In our project, we create two files in this folder.

The first one is responsible to the function of generating fake data.

//
Code Block
languagejs
themeConfluenceFadeToGrey
title
mock.js
const fakeData = require('./fakedata.js');
module.exports = {
    //Mock json
    'customer_info': fakeData.customer,
    'alarm_formdata_multiple': fakeData.customer,
    'home': fakeData.home,
    'language': fakeData.language,
}

...

//
Code Block
languagejs
themeFadeToGreyConfluence
title
mock.js
const fakeData = require('./fakedata.js');
module.exports = {
    //Mock json
    'customer_info': fakeData.customer,
    'alarm_formdata_multiple': fakeData.customer,
    'home': fakeData.home,
    'language': fakeData.language,
}

...

// import fake data object and merge it into the existed data object
Code Block
languagejs
themeConfluenceFadeToGrey
titleImport fake data object and merge it into the existed data object
const fakeoriginalData = require('./fake/mock.js');  //import datas createdgenerated in fakedata.js
………..
   files.forEach((filename) => {
          …………….
 
          if (isDir) {
             fileDisplay(filedir);
          }
          Object.keys(fakeoriginalData).map(item => {
              localJsonDb[item] = fakeoriginalData[item];
          })
    })

By all these operations above, we have the capacity to support a totally independent frontend development. Hurray! Hurray!

8.2 Customized Configuration

After met the basic requirement, we have to solve the difficulties discussed in Chapter 5.

8.2.1 Customized Request Routes

As we know that json-server can read the request routes automatically, however, if those paths are irregular’,the basic methods will not work. For example, the program can not handle this kind of request:

Code Block
languagejs
themeConfluence
createServiceType: this.baseUrl + "/uui-lcm/customers/*_*/service-subscriptions/*+*", 
getCustomerresourceVersion: this.baseUrl + "/uui-lcm/customers/*_*", 
getServiceTypeResourceVersion: this.baseUrl + "/uui-lcm/customers/*_*/service-subscriptions/*+*",


The *_*  and  *+* represent variables. The routes above contains several variables which makes it hard for json-server to handle automatically, so that’s why we need routes.js and configure all the specially routes in it:

Code Block
languagejs
themeConfluence
titleroutes.js
"/uui-lcm/serviceNumByServiceType/:customer": "/CustomersColumn",
"/uui-lcm/customers/:customer": "/getCustomerresourceVersion”,
"/uui-lcm/customers/:customer/service-subscriptions/:id": "/getServiceTypeResourceVersion",

By configuring, we clarify all the irregular routes. Next, we just need to import the file into server.js and use it as Chapter 8.1.2

8.2.2 Interface Interception

As we have discussed in Chapter 5, in this project, one single API route can be used by several different request methods and all these requests have their specific response data format which is totally different from that provided by json-server. For example:

Code Block
languagejs
themeConfluence
titleService Module
    customers: this.baseUrl + "/uui-lcm/customers", /* get */
    deleteCustomer: this.baseUrl + "/uui-lcm/customers", /* delete */
    createCustomer: this.baseUrl + "/uui-lcm/customers/", /* put */
    ……………
 
    getAllCustomers() {
        return this.http.get<any>(this.url.customers);
    }
 
    createCustomer(customer, createParams) {
        let url = this.url.createCustomer + customer;
        return this.http.put(url, createParams);
    }
 
    deleteSelectCustomer(paramsObj) {
        let url = this.url.deleteCustomer;
        let params = new HttpParams({ fromObject: paramsObj });
        return this.http.delete(url, { params });
    }


As the codes shown, ‘/uui-lcm/customers’ is used by PUT, DELETE and GET method. The program must intercept all those routes and mark them with different request methods, otherwise json-server would not return the response data we create and since we ignore the db.json which is to contain the mock data json-server created, there will be error 404. Here’s the codes in server.js:

Code Block
languagejs
themeConfluence
titleserver.js
const fs = require('fs');
const path = require('path');
const jsonServer = require('json-server');
const server = jsonServer.create();
const middlewares = jsonServer.defaults();
const customersRouters = require('./routes');
const baseUrl = "/usecaseui-server/v1";
…………
server.post(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/POST${prefix}`;
    req.method = 'GET';
    next();
})
server.put(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/PUT${prefix}`;
    req.method = 'GET';
    next();
})
server.delete(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/DELETE${prefix}`;
    req.method = 'GET';
    next();
})
……………


The codes above uses json-server to intercept data interfaces of different request methods and then rewrite the previous request routes, as well as switch all the request methods to GET. Later, we have to change routes.js and specific json files names to make the changes work.

Code Block
languagejs
themeConfluence
titleroutes.js
"/PUT/uui-lcm/customers/:name/service-subscriptions/:id": "/PUT_uui-lcm_customers_service-subscriptions”,
"/DELETE/uui-lcm/customers/:customer/service-subscriptions/:id": "/DELETE_uui-lcm_customers_service-subscriptions",

Image Added

Image 4


The reason why we do these changes is that at the developing period of a project, frontenders only have to be focused on the request parameters and response data. In other words, request methods means little for a frontender in this period so that we transfer all the request methods to GET temporarily to make mock server normally work.


Now, we have achieved all the optimization goals and a mock server can finally run healthily by one command. Hurray! Hurray! Hurray!

9. Final Result

After the optimization, the mock data scheme has been built. This new scheme supports ‘one command startup’ and allows frontenders develop independently when the backenders are not ready. And of course, this scheme perfectly reuses the original mock data files which makes this optimization the least cost.
At last, I’d like to show the final result of our work. You can also see the codes in the Gerrit repo (https://gerrit.onap.org/r/admin/repos/usecase-ui)

The mock folder structure is as follow:

Image Added

Image 5

The related configuration in package.json is as follow:

Code Block
languagejs
themeConfluence
titlepackage.json
"scripts": {
    "ng": "ng",
    "start": "ng serve",
    "server": "ng serve --proxy-config proxy.conf.json",
    "mockproxy": "ng serve --proxy-config localproxy.conf.json",
    "mockconfig": "node ./src/app/mock/server.js --port 3002",
    "mock": "npm run mockconfig | npm run mockproxy”,
    ………
 },


The full codes of server.js is as follow:

Code Block
languagejs
themeConfluence
title server.js
const jsonServer = require('json-server');
const server = jsonServer.create();
const middlewares = jsonServer.defaults();
const customersRouters = require('./routes');
const baseUrl = "/usecaseui-server/v1";

// Set default middlewares (logger, static, cors and no-cache)
server.use(middlewares);

// Get mock data
const fs = require('fs');
const path = require('path');

let localJsonDb = {};  //import mock datas
const fakeoriginalData = require('./fake/mock.js');  //import datas created in fakedata.js
const mockFolder = './src/app/mock/json'; //mock json path folder
const filePath = path.resolve(mockFolder);

fileDisplay(filePath);

function fileDisplay(filePath) {
    let fileList = [];
    // Return filelist on based of filePath
    const files = fs.readdirSync(filePath);
    files.forEach((filename) => {
        // Get filename's absolute path
        let filedir = path.join(filePath, filename);
        // Get the file information according to the file path and return an fs.Stats object
        fs.stat(filedir, (err, stats) => {
            if (err) {
                console.warn('Get files failed......');
            } else {
                let isFile = stats.isFile(); // files
                let isDir = stats.isDirectory(); //files folder
                if (isFile) {
                    fileList.push(path.basename(filedir, '.json'));
                    fileList.forEach(item => {
                        localJsonDb[item] = getjsonContent(item);
                    })
                }
                if (isDir) {
                    console.warn("=====> DO NOT support mock data in folder");
                    fileDisplay(filedir);
                }
                Object.keys(fakeoriginalData).map(item => {
                    localJsonDb[item] = fakeoriginalData[item];
                })
            }
        })
    })
    setTimeout(() => {
        serverRewrite();
        runServer(localJsonDb);
    }, 100)
}

function getjsonContent(path) {
    let newpath = `./src/app/mock/json/${path}.json`;
    let result = JSON.parse(fs.readFileSync(newpath));
    return result;
}

//only multi router data needs jsonServer.rewriter
function serverRewrite() {
    server.use(jsonServer.rewriter(customersRouters))
}

function runServer(db) {
    server.use(jsonServer.router(db));
}
server.post(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/POST${prefix}`;
    req.method = 'GET';
    next();
})
server.put(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/PUT${prefix}`;
    req.method = 'GET';
    next();
})
server.delete(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/DELETE${prefix}`;
    req.method = 'GET';
    next();
})

server.listen(3002, () => {
    console.log('Mock Server is successfully running on port 3002!')
});


That's all our jobs. Looking forward to suggestion and discussion.