diff --git a/.gitignore b/.gitignore index 9cd7c76d19b..45376520959 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ src/fireedge/.vscode/launch.json src/fireedge/yarn-error.log src/fireedge/.DS_Store src/fireedge/cypress/screenshots +src/fireedge/yarn.lock src/onedb/local/ src/onedb/shared/ diff --git a/src/fireedge/cypress/fixtures/example.json b/src/fireedge/cypress/fixtures/example.json new file mode 100644 index 00000000000..da18d9352a1 --- /dev/null +++ b/src/fireedge/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/src/fireedge/cypress/integration/examples/actions.spec.js b/src/fireedge/cypress/integration/examples/actions.spec.js new file mode 100644 index 00000000000..20e12cc6dff --- /dev/null +++ b/src/fireedge/cypress/integration/examples/actions.spec.js @@ -0,0 +1,272 @@ +/// + +context('Actions', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/actions') + }) + + // https://on.cypress.io/interacting-with-elements + + it('.type() - type into a DOM element', () => { + // https://on.cypress.io/type + cy.get('.action-email') + .type('fake@email.com').should('have.value', 'fake@email.com') + + // .type() with special character sequences + .type('{leftarrow}{rightarrow}{uparrow}{downarrow}') + .type('{del}{selectall}{backspace}') + + // .type() with key modifiers + .type('{alt}{option}') //these are equivalent + .type('{ctrl}{control}') //these are equivalent + .type('{meta}{command}{cmd}') //these are equivalent + .type('{shift}') + + // Delay each keypress by 0.1 sec + .type('slow.typing@email.com', { delay: 100 }) + .should('have.value', 'slow.typing@email.com') + + cy.get('.action-disabled') + // Ignore error checking prior to type + // like whether the input is visible or disabled + .type('disabled error checking', { force: true }) + .should('have.value', 'disabled error checking') + }) + + it('.focus() - focus on a DOM element', () => { + // https://on.cypress.io/focus + cy.get('.action-focus').focus() + .should('have.class', 'focus') + .prev().should('have.attr', 'style', 'color: orange;') + }) + + it('.blur() - blur off a DOM element', () => { + // https://on.cypress.io/blur + cy.get('.action-blur').type('About to blur').blur() + .should('have.class', 'error') + .prev().should('have.attr', 'style', 'color: red;') + }) + + it('.clear() - clears an input or textarea element', () => { + // https://on.cypress.io/clear + cy.get('.action-clear').type('Clear this text') + .should('have.value', 'Clear this text') + .clear() + .should('have.value', '') + }) + + it('.submit() - submit a form', () => { + // https://on.cypress.io/submit + cy.get('.action-form') + .find('[type="text"]').type('HALFOFF') + cy.get('.action-form').submit() + .next().should('contain', 'Your form has been submitted!') + }) + + it('.click() - click on a DOM element', () => { + // https://on.cypress.io/click + cy.get('.action-btn').click() + + // You can click on 9 specific positions of an element: + // ----------------------------------- + // | topLeft top topRight | + // | | + // | | + // | | + // | left center right | + // | | + // | | + // | | + // | bottomLeft bottom bottomRight | + // ----------------------------------- + + // clicking in the center of the element is the default + cy.get('#action-canvas').click() + + cy.get('#action-canvas').click('topLeft') + cy.get('#action-canvas').click('top') + cy.get('#action-canvas').click('topRight') + cy.get('#action-canvas').click('left') + cy.get('#action-canvas').click('right') + cy.get('#action-canvas').click('bottomLeft') + cy.get('#action-canvas').click('bottom') + cy.get('#action-canvas').click('bottomRight') + + // .click() accepts an x and y coordinate + // that controls where the click occurs :) + + cy.get('#action-canvas') + .click(80, 75) // click 80px on x coord and 75px on y coord + .click(170, 75) + .click(80, 165) + .click(100, 185) + .click(125, 190) + .click(150, 185) + .click(170, 165) + + // click multiple elements by passing multiple: true + cy.get('.action-labels>.label').click({ multiple: true }) + + // Ignore error checking prior to clicking + cy.get('.action-opacity>.btn').click({ force: true }) + }) + + it('.dblclick() - double click on a DOM element', () => { + // https://on.cypress.io/dblclick + + // Our app has a listener on 'dblclick' event in our 'scripts.js' + // that hides the div and shows an input on double click + cy.get('.action-div').dblclick().should('not.be.visible') + cy.get('.action-input-hidden').should('be.visible') + }) + + it('.check() - check a checkbox or radio element', () => { + // https://on.cypress.io/check + + // By default, .check() will check all + // matching checkbox or radio elements in succession, one after another + cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]') + .check().should('be.checked') + + cy.get('.action-radios [type="radio"]').not('[disabled]') + .check().should('be.checked') + + // .check() accepts a value argument + cy.get('.action-radios [type="radio"]') + .check('radio1').should('be.checked') + + // .check() accepts an array of values + cy.get('.action-multiple-checkboxes [type="checkbox"]') + .check(['checkbox1', 'checkbox2']).should('be.checked') + + // Ignore error checking prior to checking + cy.get('.action-checkboxes [disabled]') + .check({ force: true }).should('be.checked') + + cy.get('.action-radios [type="radio"]') + .check('radio3', { force: true }).should('be.checked') + }) + + it('.uncheck() - uncheck a checkbox element', () => { + // https://on.cypress.io/uncheck + + // By default, .uncheck() will uncheck all matching + // checkbox elements in succession, one after another + cy.get('.action-check [type="checkbox"]') + .not('[disabled]') + .uncheck().should('not.be.checked') + + // .uncheck() accepts a value argument + cy.get('.action-check [type="checkbox"]') + .check('checkbox1') + .uncheck('checkbox1').should('not.be.checked') + + // .uncheck() accepts an array of values + cy.get('.action-check [type="checkbox"]') + .check(['checkbox1', 'checkbox3']) + .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked') + + // Ignore error checking prior to unchecking + cy.get('.action-check [disabled]') + .uncheck({ force: true }).should('not.be.checked') + }) + + it('.select() - select an option in a element', () => { + // https://on.cypress.io/select + + // Select option(s) with matching text content + cy.get('.action-select').select('apples') + + cy.get('.action-select-multiple') + .select(['apples', 'oranges', 'bananas']) + + // Select option(s) with matching value + cy.get('.action-select').select('fr-bananas') + + cy.get('.action-select-multiple') + .select(['fr-apples', 'fr-oranges', 'fr-bananas']) + }) + + it('.scrollIntoView() - scroll an element into view', () => { + // https://on.cypress.io/scrollintoview + + // normally all of these buttons are hidden, + // because they're not within + // the viewable area of their parent + // (we need to scroll to see them) + cy.get('#scroll-horizontal button') + .should('not.be.visible') + + // scroll the button into view, as if the user had scrolled + cy.get('#scroll-horizontal button').scrollIntoView() + .should('be.visible') + + cy.get('#scroll-vertical button') + .should('not.be.visible') + + // Cypress handles the scroll direction needed + cy.get('#scroll-vertical button').scrollIntoView() + .should('be.visible') + + cy.get('#scroll-both button') + .should('not.be.visible') + + // Cypress knows to scroll to the right and down + cy.get('#scroll-both button').scrollIntoView() + .should('be.visible') + }) + + it('.trigger() - trigger an event on a DOM element', () => { + // https://on.cypress.io/trigger + + // To interact with a range input (slider) + // we need to set its value & trigger the + // event to signal it changed + + // Here, we invoke jQuery's val() method to set + // the value and trigger the 'change' event + cy.get('.trigger-input-range') + .invoke('val', 25) + .trigger('change') + .get('input[type=range]').siblings('p') + .should('have.text', '25') + }) + + it('cy.scrollTo() - scroll the window or element to a position', () => { + + // https://on.cypress.io/scrollTo + + // You can scroll to 9 specific positions of an element: + // ----------------------------------- + // | topLeft top topRight | + // | | + // | | + // | | + // | left center right | + // | | + // | | + // | | + // | bottomLeft bottom bottomRight | + // ----------------------------------- + + // if you chain .scrollTo() off of cy, we will + // scroll the entire window + cy.scrollTo('bottom') + + cy.get('#scrollable-horizontal').scrollTo('right') + + // or you can scroll to a specific coordinate: + // (x axis, y axis) in pixels + cy.get('#scrollable-vertical').scrollTo(250, 250) + + // or you can scroll to a specific percentage + // of the (width, height) of the element + cy.get('#scrollable-both').scrollTo('75%', '25%') + + // control the easing of the scroll (default is 'swing') + cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' }) + + // control the duration of the scroll (in ms) + cy.get('#scrollable-both').scrollTo('center', { duration: 2000 }) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/aliasing.spec.js b/src/fireedge/cypress/integration/examples/aliasing.spec.js new file mode 100644 index 00000000000..95bac735c44 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/aliasing.spec.js @@ -0,0 +1,42 @@ +/// + +context('Aliasing', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/aliasing') + }) + + it('.as() - alias a DOM element for later use', () => { + // https://on.cypress.io/as + + // Alias a DOM element for use later + // We don't have to traverse to the element + // later in our code, we reference it with @ + + cy.get('.as-table').find('tbody>tr') + .first().find('td').first() + .find('button').as('firstBtn') + + // when we reference the alias, we place an + // @ in front of its name + cy.get('@firstBtn').click() + + cy.get('@firstBtn') + .should('have.class', 'btn-success') + .and('contain', 'Changed') + }) + + it('.as() - alias a route for later use', () => { + + // Alias the route to wait for its response + cy.server() + cy.route('GET', 'comments/*').as('getComment') + + // we have code that gets a comment when + // the button is clicked in scripts.js + cy.get('.network-btn').click() + + // https://on.cypress.io/wait + cy.wait('@getComment').its('status').should('eq', 200) + + }) +}) diff --git a/src/fireedge/cypress/integration/examples/assertions.spec.js b/src/fireedge/cypress/integration/examples/assertions.spec.js new file mode 100644 index 00000000000..791383b6651 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/assertions.spec.js @@ -0,0 +1,168 @@ +/// + +context('Assertions', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/assertions') + }) + + describe('Implicit Assertions', () => { + it('.should() - make an assertion about the current subject', () => { + // https://on.cypress.io/should + cy.get('.assertion-table') + .find('tbody tr:last') + .should('have.class', 'success') + .find('td') + .first() + // checking the text of the element in various ways + .should('have.text', 'Column content') + .should('contain', 'Column content') + .should('have.html', 'Column content') + // chai-jquery uses "is()" to check if element matches selector + .should('match', 'td') + // to match text content against a regular expression + // first need to invoke jQuery method text() + // and then match using regular expression + .invoke('text') + .should('match', /column content/i) + + // a better way to check element's text content against a regular expression + // is to use "cy.contains" + // https://on.cypress.io/contains + cy.get('.assertion-table') + .find('tbody tr:last') + // finds first element with text content matching regular expression + .contains('td', /column content/i) + .should('be.visible') + + // for more information about asserting element's text + // see https://on.cypress.io/using-cypress-faq#How-do-I-get-an-element’s-text-contents + }) + + it('.and() - chain multiple assertions together', () => { + // https://on.cypress.io/and + cy.get('.assertions-link') + .should('have.class', 'active') + .and('have.attr', 'href') + .and('include', 'cypress.io') + }) + }) + + describe('Explicit Assertions', () => { + // https://on.cypress.io/assertions + it('expect - make an assertion about a specified subject', () => { + // We can use Chai's BDD style assertions + expect(true).to.be.true + const o = { foo: 'bar' } + + expect(o).to.equal(o) + expect(o).to.deep.equal({ foo: 'bar' }) + // matching text using regular expression + expect('FooBar').to.match(/bar$/i) + }) + + it('pass your own callback function to should()', () => { + // Pass a function to should that can have any number + // of explicit assertions within it. + // The ".should(cb)" function will be retried + // automatically until it passes all your explicit assertions or times out. + cy.get('.assertions-p') + .find('p') + .should(($p) => { + // https://on.cypress.io/$ + // return an array of texts from all of the p's + // @ts-ignore TS6133 unused variable + const texts = $p.map((i, el) => Cypress.$(el).text()) + + // jquery map returns jquery object + // and .get() convert this to simple array + const paragraphs = texts.get() + + // array should have length of 3 + expect(paragraphs, 'has 3 paragraphs').to.have.length(3) + + // use second argument to expect(...) to provide clear + // message with each assertion + expect(paragraphs, 'has expected text in each paragraph').to.deep.eq([ + 'Some text from first p', + 'More text from second p', + 'And even more text from third p', + ]) + }) + }) + + it('finds element by class name regex', () => { + cy.get('.docs-header') + .find('div') + // .should(cb) callback function will be retried + .should(($div) => { + expect($div).to.have.length(1) + + const className = $div[0].className + + expect(className).to.match(/heading-/) + }) + // .then(cb) callback is not retried, + // it either passes or fails + .then(($div) => { + expect($div, 'text content').to.have.text('Introduction') + }) + }) + + it('can throw any error', () => { + cy.get('.docs-header') + .find('div') + .should(($div) => { + if ($div.length !== 1) { + // you can throw your own errors + throw new Error('Did not find 1 element') + } + + const className = $div[0].className + + if (!className.match(/heading-/)) { + throw new Error(`Could not find class "heading-" in ${className}`) + } + }) + }) + + it('matches unknown text between two elements', () => { + /** + * Text from the first element. + * @type {string} + */ + let text + + /** + * Normalizes passed text, + * useful before comparing text with spaces and different capitalization. + * @param {string} s Text to normalize + */ + const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase() + + cy.get('.two-elements') + .find('.first') + .then(($first) => { + // save text from the first element + text = normalizeText($first.text()) + }) + + cy.get('.two-elements') + .find('.second') + .should(($div) => { + // we can massage text before comparing + const secondText = normalizeText($div.text()) + + expect(secondText, 'second text').to.equal(text) + }) + }) + + it('assert - assert shape of an object', () => { + const person = { + name: 'Joe', + age: 20, + } + + assert.isObject(person, 'value is object') + }) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/connectors.spec.js b/src/fireedge/cypress/integration/examples/connectors.spec.js new file mode 100644 index 00000000000..26deb7ade68 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/connectors.spec.js @@ -0,0 +1,56 @@ +/// + +context('Connectors', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/connectors') + }) + + it('.each() - iterate over an array of elements', () => { + // https://on.cypress.io/each + cy.get('.connectors-each-ul>li') + .each(($el, index, $list) => { + console.log($el, index, $list) + }) + }) + + it('.its() - get properties on the current subject', () => { + // https://on.cypress.io/its + cy.get('.connectors-its-ul>li') + // calls the 'length' property yielding that value + .its('length') + .should('be.gt', 2) + }) + + it('.invoke() - invoke a function on the current subject', () => { + // our div is hidden in our script.js + // $('.connectors-div').hide() + + // https://on.cypress.io/invoke + cy.get('.connectors-div').should('be.hidden') + // call the jquery method 'show' on the 'div.container' + .invoke('show') + .should('be.visible') + }) + + it('.spread() - spread an array as individual args to callback function', () => { + // https://on.cypress.io/spread + const arr = ['foo', 'bar', 'baz'] + + cy.wrap(arr).spread((foo, bar, baz) => { + expect(foo).to.eq('foo') + expect(bar).to.eq('bar') + expect(baz).to.eq('baz') + }) + }) + + it('.then() - invoke a callback function with the current subject', () => { + // https://on.cypress.io/then + cy.get('.connectors-list > li') + .then(($lis) => { + expect($lis, '3 items').to.have.length(3) + expect($lis.eq(0), 'first item').to.contain('Walk the dog') + expect($lis.eq(1), 'second item').to.contain('Feed the cat') + expect($lis.eq(2), 'third item').to.contain('Write JavaScript') + }) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/cookies.spec.js b/src/fireedge/cypress/integration/examples/cookies.spec.js new file mode 100644 index 00000000000..bb540e95eb0 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/cookies.spec.js @@ -0,0 +1,78 @@ +/// + +context('Cookies', () => { + beforeEach(() => { + Cypress.Cookies.debug(true) + + cy.visit('https://example.cypress.io/commands/cookies') + + // clear cookies again after visiting to remove + // any 3rd party cookies picked up such as cloudflare + cy.clearCookies() + }) + + it('cy.getCookie() - get a browser cookie', () => { + // https://on.cypress.io/getcookie + cy.get('#getCookie .set-a-cookie').click() + + // cy.getCookie() yields a cookie object + cy.getCookie('token').should('have.property', 'value', '123ABC') + }) + + it('cy.getCookies() - get browser cookies', () => { + // https://on.cypress.io/getcookies + cy.getCookies().should('be.empty') + + cy.get('#getCookies .set-a-cookie').click() + + // cy.getCookies() yields an array of cookies + cy.getCookies().should('have.length', 1).should((cookies) => { + + // each cookie has these properties + expect(cookies[0]).to.have.property('name', 'token') + expect(cookies[0]).to.have.property('value', '123ABC') + expect(cookies[0]).to.have.property('httpOnly', false) + expect(cookies[0]).to.have.property('secure', false) + expect(cookies[0]).to.have.property('domain') + expect(cookies[0]).to.have.property('path') + }) + }) + + it('cy.setCookie() - set a browser cookie', () => { + // https://on.cypress.io/setcookie + cy.getCookies().should('be.empty') + + cy.setCookie('foo', 'bar') + + // cy.getCookie() yields a cookie object + cy.getCookie('foo').should('have.property', 'value', 'bar') + }) + + it('cy.clearCookie() - clear a browser cookie', () => { + // https://on.cypress.io/clearcookie + cy.getCookie('token').should('be.null') + + cy.get('#clearCookie .set-a-cookie').click() + + cy.getCookie('token').should('have.property', 'value', '123ABC') + + // cy.clearCookies() yields null + cy.clearCookie('token').should('be.null') + + cy.getCookie('token').should('be.null') + }) + + it('cy.clearCookies() - clear browser cookies', () => { + // https://on.cypress.io/clearcookies + cy.getCookies().should('be.empty') + + cy.get('#clearCookies .set-a-cookie').click() + + cy.getCookies().should('have.length', 1) + + // cy.clearCookies() yields null + cy.clearCookies() + + cy.getCookies().should('be.empty') + }) +}) diff --git a/src/fireedge/cypress/integration/examples/cypress_api.spec.js b/src/fireedge/cypress/integration/examples/cypress_api.spec.js new file mode 100644 index 00000000000..0e46520cd16 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/cypress_api.spec.js @@ -0,0 +1,222 @@ +/// + +context('Cypress.Commands', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + // https://on.cypress.io/custom-commands + + it('.add() - create a custom command', () => { + Cypress.Commands.add('console', { + prevSubject: true, + }, (subject, method) => { + // the previous subject is automatically received + // and the commands arguments are shifted + + // allow us to change the console method used + method = method || 'log' + + // log the subject to the console + // @ts-ignore TS7017 + console[method]('The subject is', subject) + + // whatever we return becomes the new subject + // we don't want to change the subject so + // we return whatever was passed in + return subject + }) + + // @ts-ignore TS2339 + cy.get('button').console('info').then(($button) => { + // subject is still $button + }) + }) +}) + + +context('Cypress.Cookies', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + // https://on.cypress.io/cookies + it('.debug() - enable or disable debugging', () => { + Cypress.Cookies.debug(true) + + // Cypress will now log in the console when + // cookies are set or cleared + cy.setCookie('fakeCookie', '123ABC') + cy.clearCookie('fakeCookie') + cy.setCookie('fakeCookie', '123ABC') + cy.clearCookie('fakeCookie') + cy.setCookie('fakeCookie', '123ABC') + }) + + it('.preserveOnce() - preserve cookies by key', () => { + // normally cookies are reset after each test + cy.getCookie('fakeCookie').should('not.be.ok') + + // preserving a cookie will not clear it when + // the next test starts + cy.setCookie('lastCookie', '789XYZ') + Cypress.Cookies.preserveOnce('lastCookie') + }) + + it('.defaults() - set defaults for all cookies', () => { + // now any cookie with the name 'session_id' will + // not be cleared before each new test runs + Cypress.Cookies.defaults({ + whitelist: 'session_id', + }) + }) +}) + + +context('Cypress.Server', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + // Permanently override server options for + // all instances of cy.server() + + // https://on.cypress.io/cypress-server + it('.defaults() - change default config of server', () => { + Cypress.Server.defaults({ + delay: 0, + force404: false, + }) + }) +}) + +context('Cypress.arch', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + it('Get CPU architecture name of underlying OS', () => { + // https://on.cypress.io/arch + expect(Cypress.arch).to.exist + }) +}) + +context('Cypress.config()', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + it('Get and set configuration options', () => { + // https://on.cypress.io/config + let myConfig = Cypress.config() + + expect(myConfig).to.have.property('animationDistanceThreshold', 5) + expect(myConfig).to.have.property('baseUrl', null) + expect(myConfig).to.have.property('defaultCommandTimeout', 4000) + expect(myConfig).to.have.property('requestTimeout', 5000) + expect(myConfig).to.have.property('responseTimeout', 30000) + expect(myConfig).to.have.property('viewportHeight', 660) + expect(myConfig).to.have.property('viewportWidth', 1000) + expect(myConfig).to.have.property('pageLoadTimeout', 60000) + expect(myConfig).to.have.property('waitForAnimations', true) + + expect(Cypress.config('pageLoadTimeout')).to.eq(60000) + + // this will change the config for the rest of your tests! + Cypress.config('pageLoadTimeout', 20000) + + expect(Cypress.config('pageLoadTimeout')).to.eq(20000) + + Cypress.config('pageLoadTimeout', 60000) + }) +}) + +context('Cypress.dom', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + // https://on.cypress.io/dom + it('.isHidden() - determine if a DOM element is hidden', () => { + let hiddenP = Cypress.$('.dom-p p.hidden').get(0) + let visibleP = Cypress.$('.dom-p p.visible').get(0) + + // our first paragraph has css class 'hidden' + expect(Cypress.dom.isHidden(hiddenP)).to.be.true + expect(Cypress.dom.isHidden(visibleP)).to.be.false + }) +}) + +context('Cypress.env()', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + // We can set environment variables for highly dynamic values + + // https://on.cypress.io/environment-variables + it('Get environment variables', () => { + // https://on.cypress.io/env + // set multiple environment variables + Cypress.env({ + host: 'veronica.dev.local', + api_server: 'http://localhost:8888/v1/', + }) + + // get environment variable + expect(Cypress.env('host')).to.eq('veronica.dev.local') + + // set environment variable + Cypress.env('api_server', 'http://localhost:8888/v2/') + expect(Cypress.env('api_server')).to.eq('http://localhost:8888/v2/') + + // get all environment variable + expect(Cypress.env()).to.have.property('host', 'veronica.dev.local') + expect(Cypress.env()).to.have.property('api_server', 'http://localhost:8888/v2/') + }) +}) + +context('Cypress.log', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + it('Control what is printed to the Command Log', () => { + // https://on.cypress.io/cypress-log + }) +}) + + +context('Cypress.platform', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + it('Get underlying OS name', () => { + // https://on.cypress.io/platform + expect(Cypress.platform).to.be.exist + }) +}) + +context('Cypress.version', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + it('Get current version of Cypress being run', () => { + // https://on.cypress.io/version + expect(Cypress.version).to.be.exist + }) +}) + +context('Cypress.spec', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/cypress-api') + }) + + it('Get current spec information', () => { + // https://on.cypress.io/spec + // wrap the object so we can inspect it easily by clicking in the command log + cy.wrap(Cypress.spec).should('have.keys', ['name', 'relative', 'absolute']) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/files.spec.js b/src/fireedge/cypress/integration/examples/files.spec.js new file mode 100644 index 00000000000..c1a9ab2f48e --- /dev/null +++ b/src/fireedge/cypress/integration/examples/files.spec.js @@ -0,0 +1,114 @@ +/// + +/// JSON fixture file can be loaded directly using +// the built-in JavaScript bundler +// @ts-ignore +const requiredExample = require('../../fixtures/example') + +context('Files', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/files') + }) + + beforeEach(() => { + // load example.json fixture file and store + // in the test context object + cy.fixture('example.json').as('example') + }) + + it('cy.fixture() - load a fixture', () => { + // https://on.cypress.io/fixture + + // Instead of writing a response inline you can + // use a fixture file's content. + + cy.server() + cy.fixture('example.json').as('comment') + // when application makes an Ajax request matching "GET comments/*" + // Cypress will intercept it and reply with object + // from the "comment" alias + cy.route('GET', 'comments/*', '@comment').as('getComment') + + // we have code that gets a comment when + // the button is clicked in scripts.js + cy.get('.fixture-btn').click() + + cy.wait('@getComment').its('responseBody') + .should('have.property', 'name') + .and('include', 'Using fixtures to represent data') + + // you can also just write the fixture in the route + cy.route('GET', 'comments/*', 'fixture:example.json').as('getComment') + + // we have code that gets a comment when + // the button is clicked in scripts.js + cy.get('.fixture-btn').click() + + cy.wait('@getComment').its('responseBody') + .should('have.property', 'name') + .and('include', 'Using fixtures to represent data') + + // or write fx to represent fixture + // by default it assumes it's .json + cy.route('GET', 'comments/*', 'fx:example').as('getComment') + + // we have code that gets a comment when + // the button is clicked in scripts.js + cy.get('.fixture-btn').click() + + cy.wait('@getComment').its('responseBody') + .should('have.property', 'name') + .and('include', 'Using fixtures to represent data') + }) + + it('cy.fixture() or require - load a fixture', function () { + // we are inside the "function () { ... }" + // callback and can use test context object "this" + // "this.example" was loaded in "beforeEach" function callback + expect(this.example, 'fixture in the test context') + .to.deep.equal(requiredExample) + + // or use "cy.wrap" and "should('deep.equal', ...)" assertion + // @ts-ignore + cy.wrap(this.example, 'fixture vs require') + .should('deep.equal', requiredExample) + }) + + it('cy.readFile() - read a files contents', () => { + // https://on.cypress.io/readfile + + // You can read a file and yield its contents + // The filePath is relative to your project's root. + cy.readFile('cypress.json').then((json) => { + expect(json).to.be.an('object') + }) + }) + + it('cy.writeFile() - write to a file', () => { + // https://on.cypress.io/writefile + + // You can write to a file + + // Use a response from a request to automatically + // generate a fixture file for use later + cy.request('https://jsonplaceholder.cypress.io/users') + .then((response) => { + cy.writeFile('cypress/fixtures/users.json', response.body) + }) + cy.fixture('users').should((users) => { + expect(users[0].name).to.exist + }) + + // JavaScript arrays and objects are stringified + // and formatted into text. + cy.writeFile('cypress/fixtures/profile.json', { + id: 8739, + name: 'Jane', + email: 'jane@example.com', + }) + + cy.fixture('profile').should((profile) => { + expect(profile.name).to.eq('Jane') + }) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/local_storage.spec.js b/src/fireedge/cypress/integration/examples/local_storage.spec.js new file mode 100644 index 00000000000..076b096fc37 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/local_storage.spec.js @@ -0,0 +1,52 @@ +/// + +context('Local Storage', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/local-storage') + }) + // Although local storage is automatically cleared + // in between tests to maintain a clean state + // sometimes we need to clear the local storage manually + + it('cy.clearLocalStorage() - clear all data in local storage', () => { + // https://on.cypress.io/clearlocalstorage + cy.get('.ls-btn').click().should(() => { + expect(localStorage.getItem('prop1')).to.eq('red') + expect(localStorage.getItem('prop2')).to.eq('blue') + expect(localStorage.getItem('prop3')).to.eq('magenta') + }) + + // clearLocalStorage() yields the localStorage object + cy.clearLocalStorage().should((ls) => { + expect(ls.getItem('prop1')).to.be.null + expect(ls.getItem('prop2')).to.be.null + expect(ls.getItem('prop3')).to.be.null + }) + + // Clear key matching string in Local Storage + cy.get('.ls-btn').click().should(() => { + expect(localStorage.getItem('prop1')).to.eq('red') + expect(localStorage.getItem('prop2')).to.eq('blue') + expect(localStorage.getItem('prop3')).to.eq('magenta') + }) + + cy.clearLocalStorage('prop1').should((ls) => { + expect(ls.getItem('prop1')).to.be.null + expect(ls.getItem('prop2')).to.eq('blue') + expect(ls.getItem('prop3')).to.eq('magenta') + }) + + // Clear keys matching regex in Local Storage + cy.get('.ls-btn').click().should(() => { + expect(localStorage.getItem('prop1')).to.eq('red') + expect(localStorage.getItem('prop2')).to.eq('blue') + expect(localStorage.getItem('prop3')).to.eq('magenta') + }) + + cy.clearLocalStorage(/prop1|2/).should((ls) => { + expect(ls.getItem('prop1')).to.be.null + expect(ls.getItem('prop2')).to.be.null + expect(ls.getItem('prop3')).to.eq('magenta') + }) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/location.spec.js b/src/fireedge/cypress/integration/examples/location.spec.js new file mode 100644 index 00000000000..68e755c101c --- /dev/null +++ b/src/fireedge/cypress/integration/examples/location.spec.js @@ -0,0 +1,32 @@ +/// + +context('Location', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/location') + }) + + it('cy.hash() - get the current URL hash', () => { + // https://on.cypress.io/hash + cy.hash().should('be.empty') + }) + + it('cy.location() - get window.location', () => { + // https://on.cypress.io/location + cy.location().should((location) => { + expect(location.hash).to.be.empty + expect(location.href).to.eq('https://example.cypress.io/commands/location') + expect(location.host).to.eq('example.cypress.io') + expect(location.hostname).to.eq('example.cypress.io') + expect(location.origin).to.eq('https://example.cypress.io') + expect(location.pathname).to.eq('/commands/location') + expect(location.port).to.eq('') + expect(location.protocol).to.eq('https:') + expect(location.search).to.be.empty + }) + }) + + it('cy.url() - get the current URL', () => { + // https://on.cypress.io/url + cy.url().should('eq', 'https://example.cypress.io/commands/location') + }) +}) diff --git a/src/fireedge/cypress/integration/examples/misc.spec.js b/src/fireedge/cypress/integration/examples/misc.spec.js new file mode 100644 index 00000000000..97edd8be0a4 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/misc.spec.js @@ -0,0 +1,83 @@ +/// + +context('Misc', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/misc') + }) + + it('.end() - end the command chain', () => { + // https://on.cypress.io/end + + // cy.end is useful when you want to end a chain of commands + // and force Cypress to re-query from the root element + cy.get('.misc-table').within(() => { + // ends the current chain and yields null + cy.contains('Cheryl').click().end() + + // queries the entire table again + cy.contains('Charles').click() + }) + }) + + it('cy.exec() - execute a system command', () => { + // https://on.cypress.io/exec + + // execute a system command. + // so you can take actions necessary for + // your test outside the scope of Cypress. + cy.exec('echo Jane Lane') + .its('stdout').should('contain', 'Jane Lane') + + // we can use Cypress.platform string to + // select appropriate command + // https://on.cypress/io/platform + cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`) + + if (Cypress.platform === 'win32') { + cy.exec('print cypress.json') + .its('stderr').should('be.empty') + } else { + cy.exec('cat cypress.json') + .its('stderr').should('be.empty') + + cy.exec('pwd') + .its('code').should('eq', 0) + } + }) + + it('cy.focused() - get the DOM element that has focus', () => { + // https://on.cypress.io/focused + cy.get('.misc-form').find('#name').click() + cy.focused().should('have.id', 'name') + + cy.get('.misc-form').find('#description').click() + cy.focused().should('have.id', 'description') + }) + + context('Cypress.Screenshot', function () { + it('cy.screenshot() - take a screenshot', () => { + // https://on.cypress.io/screenshot + cy.screenshot('my-image') + }) + + it('Cypress.Screenshot.defaults() - change default config of screenshots', function () { + Cypress.Screenshot.defaults({ + blackout: ['.foo'], + capture: 'viewport', + clip: { x: 0, y: 0, width: 200, height: 200 }, + scale: false, + disableTimersAndAnimations: true, + screenshotOnRunFailure: true, + beforeScreenshot () { }, + afterScreenshot () { }, + }) + }) + }) + + it('cy.wrap() - wrap an object', () => { + // https://on.cypress.io/wrap + cy.wrap({ foo: 'bar' }) + .should('have.property', 'foo') + .and('include', 'bar') + }) +}) diff --git a/src/fireedge/cypress/integration/examples/navigation.spec.js b/src/fireedge/cypress/integration/examples/navigation.spec.js new file mode 100644 index 00000000000..bbd9d479c81 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/navigation.spec.js @@ -0,0 +1,56 @@ +/// + +context('Navigation', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io') + cy.get('.navbar-nav').contains('Commands').click() + cy.get('.dropdown-menu').contains('Navigation').click() + }) + + it('cy.go() - go back or forward in the browser\'s history', () => { + // https://on.cypress.io/go + + cy.location('pathname').should('include', 'navigation') + + cy.go('back') + cy.location('pathname').should('not.include', 'navigation') + + cy.go('forward') + cy.location('pathname').should('include', 'navigation') + + // clicking back + cy.go(-1) + cy.location('pathname').should('not.include', 'navigation') + + // clicking forward + cy.go(1) + cy.location('pathname').should('include', 'navigation') + }) + + it('cy.reload() - reload the page', () => { + // https://on.cypress.io/reload + cy.reload() + + // reload the page without using the cache + cy.reload(true) + }) + + it('cy.visit() - visit a remote url', () => { + // https://on.cypress.io/visit + + // Visit any sub-domain of your current domain + + // Pass options to the visit + cy.visit('https://example.cypress.io/commands/navigation', { + timeout: 50000, // increase total time for the visit to resolve + onBeforeLoad (contentWindow) { + // contentWindow is the remote page's window object + expect(typeof contentWindow === 'object').to.be.true + }, + onLoad (contentWindow) { + // contentWindow is the remote page's window object + expect(typeof contentWindow === 'object').to.be.true + }, + }) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/network_requests.spec.js b/src/fireedge/cypress/integration/examples/network_requests.spec.js new file mode 100644 index 00000000000..4fa12588cfb --- /dev/null +++ b/src/fireedge/cypress/integration/examples/network_requests.spec.js @@ -0,0 +1,194 @@ +/// + +context('Network Requests', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/network-requests') + }) + + // Manage AJAX / XHR requests in your app + + it('cy.server() - control behavior of network requests and responses', () => { + // https://on.cypress.io/server + + cy.server().should((server) => { + // the default options on server + // you can override any of these options + expect(server.delay).to.eq(0) + expect(server.method).to.eq('GET') + expect(server.status).to.eq(200) + expect(server.headers).to.be.null + expect(server.response).to.be.null + expect(server.onRequest).to.be.undefined + expect(server.onResponse).to.be.undefined + expect(server.onAbort).to.be.undefined + + // These options control the server behavior + // affecting all requests + + // pass false to disable existing route stubs + expect(server.enable).to.be.true + // forces requests that don't match your routes to 404 + expect(server.force404).to.be.false + // whitelists requests from ever being logged or stubbed + expect(server.whitelist).to.be.a('function') + }) + + cy.server({ + method: 'POST', + delay: 1000, + status: 422, + response: {}, + }) + + // any route commands will now inherit the above options + // from the server. anything we pass specifically + // to route will override the defaults though. + }) + + it('cy.request() - make an XHR request', () => { + // https://on.cypress.io/request + cy.request('https://jsonplaceholder.cypress.io/comments') + .should((response) => { + expect(response.status).to.eq(200) + expect(response.body).to.have.length(500) + expect(response).to.have.property('headers') + expect(response).to.have.property('duration') + }) + }) + + + it('cy.request() - verify response using BDD syntax', () => { + cy.request('https://jsonplaceholder.cypress.io/comments') + .then((response) => { + // https://on.cypress.io/assertions + expect(response).property('status').to.equal(200) + expect(response).property('body').to.have.length(500) + expect(response).to.include.keys('headers', 'duration') + }) + }) + + it('cy.request() with query parameters', () => { + // will execute request + // https://jsonplaceholder.cypress.io/comments?postId=1&id=3 + cy.request({ + url: 'https://jsonplaceholder.cypress.io/comments', + qs: { + postId: 1, + id: 3, + }, + }) + .its('body') + .should('be.an', 'array') + .and('have.length', 1) + .its('0') // yields first element of the array + .should('contain', { + postId: 1, + id: 3, + }) + }) + + it('cy.request() - pass result to the second request', () => { + // first, let's find out the userId of the first user we have + cy.request('https://jsonplaceholder.cypress.io/users?_limit=1') + .its('body.0') // yields the first element of the returned list + .then((user) => { + expect(user).property('id').to.be.a('number') + // make a new post on behalf of the user + cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', { + userId: user.id, + title: 'Cypress Test Runner', + body: 'Fast, easy and reliable testing for anything that runs in a browser.', + }) + }) + // note that the value here is the returned value of the 2nd request + // which is the new post object + .then((response) => { + expect(response).property('status').to.equal(201) // new entity created + expect(response).property('body').to.contain({ + id: 101, // there are already 100 posts, so new entity gets id 101 + title: 'Cypress Test Runner', + }) + // we don't know the user id here - since it was in above closure + // so in this test just confirm that the property is there + expect(response.body).property('userId').to.be.a('number') + }) + }) + + it('cy.request() - save response in the shared test context', () => { + // https://on.cypress.io/variables-and-aliases + cy.request('https://jsonplaceholder.cypress.io/users?_limit=1') + .its('body.0') // yields the first element of the returned list + .as('user') // saves the object in the test context + .then(function () { + // NOTE đŸ‘€ + // By the time this callback runs the "as('user')" command + // has saved the user object in the test context. + // To access the test context we need to use + // the "function () { ... }" callback form, + // otherwise "this" points at a wrong or undefined object! + cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', { + userId: this.user.id, + title: 'Cypress Test Runner', + body: 'Fast, easy and reliable testing for anything that runs in a browser.', + }) + .its('body').as('post') // save the new post from the response + }) + .then(function () { + // When this callback runs, both "cy.request" API commands have finished + // and the test context has "user" and "post" objects set. + // Let's verify them. + expect(this.post, 'post has the right user id').property('userId').to.equal(this.user.id) + }) + }) + + it('cy.route() - route responses to matching requests', () => { + // https://on.cypress.io/route + + let message = 'whoa, this comment does not exist' + + cy.server() + + // Listen to GET to comments/1 + cy.route('GET', 'comments/*').as('getComment') + + // we have code that gets a comment when + // the button is clicked in scripts.js + cy.get('.network-btn').click() + + // https://on.cypress.io/wait + cy.wait('@getComment').its('status').should('eq', 200) + + // Listen to POST to comments + cy.route('POST', '/comments').as('postComment') + + // we have code that posts a comment when + // the button is clicked in scripts.js + cy.get('.network-post').click() + cy.wait('@postComment') + + // get the route + cy.get('@postComment').should((xhr) => { + expect(xhr.requestBody).to.include('email') + expect(xhr.requestHeaders).to.have.property('Content-Type') + expect(xhr.responseBody).to.have.property('name', 'Using POST in cy.route()') + }) + + // Stub a response to PUT comments/ **** + cy.route({ + method: 'PUT', + url: 'comments/*', + status: 404, + response: { error: message }, + delay: 500, + }).as('putComment') + + // we have code that puts a comment when + // the button is clicked in scripts.js + cy.get('.network-put').click() + + cy.wait('@putComment') + + // our 404 statusCode logic in scripts.js executed + cy.get('.network-put-comment').should('contain', message) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/querying.spec.js b/src/fireedge/cypress/integration/examples/querying.spec.js new file mode 100644 index 00000000000..484a8ecab8c --- /dev/null +++ b/src/fireedge/cypress/integration/examples/querying.spec.js @@ -0,0 +1,87 @@ +/// + +context('Querying', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/querying') + }) + + // The most commonly used query is 'cy.get()', you can + // think of this like the '$' in jQuery + + it('cy.get() - query DOM elements', () => { + // https://on.cypress.io/get + + cy.get('#query-btn').should('contain', 'Button') + + cy.get('.query-btn').should('contain', 'Button') + + cy.get('#querying .well>button:first').should('contain', 'Button') + // ↲ + // Use CSS selectors just like jQuery + + cy.get('[data-test-id="test-example"]').should('have.class', 'example') + + // 'cy.get()' yields jQuery object, you can get its attribute + // by invoking `.attr()` method + cy.get('[data-test-id="test-example"]') + .invoke('attr', 'data-test-id') + .should('equal', 'test-example') + + // or you can get element's CSS property + cy.get('[data-test-id="test-example"]') + .invoke('css', 'position') + .should('equal', 'static') + + // or use assertions directly during 'cy.get()' + // https://on.cypress.io/assertions + cy.get('[data-test-id="test-example"]') + .should('have.attr', 'data-test-id', 'test-example') + .and('have.css', 'position', 'static') + }) + + it('cy.contains() - query DOM elements with matching content', () => { + // https://on.cypress.io/contains + cy.get('.query-list') + .contains('bananas') + .should('have.class', 'third') + + // we can pass a regexp to `.contains()` + cy.get('.query-list') + .contains(/^b\w+/) + .should('have.class', 'third') + + cy.get('.query-list') + .contains('apples') + .should('have.class', 'first') + + // passing a selector to contains will + // yield the selector containing the text + cy.get('#querying') + .contains('ul', 'oranges') + .should('have.class', 'query-list') + + cy.get('.query-button') + .contains('Save Form') + .should('have.class', 'btn') + }) + + it('.within() - query DOM elements within a specific element', () => { + // https://on.cypress.io/within + cy.get('.query-form').within(() => { + cy.get('input:first').should('have.attr', 'placeholder', 'Email') + cy.get('input:last').should('have.attr', 'placeholder', 'Password') + }) + }) + + it('cy.root() - query the root DOM element', () => { + // https://on.cypress.io/root + + // By default, root is the document + cy.root().should('match', 'html') + + cy.get('.query-ul').within(() => { + // In this within, the root is now the ul DOM element + cy.root().should('have.class', 'query-ul') + }) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/spies_stubs_clocks.spec.js b/src/fireedge/cypress/integration/examples/spies_stubs_clocks.spec.js new file mode 100644 index 00000000000..ee677e6a204 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/spies_stubs_clocks.spec.js @@ -0,0 +1,95 @@ +/// + +context('Spies, Stubs, and Clock', () => { + it('cy.spy() - wrap a method in a spy', () => { + // https://on.cypress.io/spy + cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') + + const obj = { + foo () {}, + } + + const spy = cy.spy(obj, 'foo').as('anyArgs') + + obj.foo() + + expect(spy).to.be.called + }) + + it('cy.spy() retries until assertions pass', () => { + cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') + + const obj = { + /** + * Prints the argument passed + * @param x {any} + */ + foo (x) { + console.log('obj.foo called with', x) + }, + } + + cy.spy(obj, 'foo').as('foo') + + setTimeout(() => { + obj.foo('first') + }, 500) + + setTimeout(() => { + obj.foo('second') + }, 2500) + + cy.get('@foo').should('have.been.calledTwice') + }) + + it('cy.stub() - create a stub and/or replace a function with stub', () => { + // https://on.cypress.io/stub + cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') + + const obj = { + /** + * prints both arguments to the console + * @param a {string} + * @param b {string} + */ + foo (a, b) { + console.log('a', a, 'b', b) + }, + } + + const stub = cy.stub(obj, 'foo').as('foo') + + obj.foo('foo', 'bar') + + expect(stub).to.be.called + }) + + it('cy.clock() - control time in the browser', () => { + // https://on.cypress.io/clock + + // create the date in UTC so its always the same + // no matter what local timezone the browser is running in + const now = new Date(Date.UTC(2017, 2, 14)).getTime() + + cy.clock(now) + cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') + cy.get('#clock-div').click() + .should('have.text', '1489449600') + }) + + it('cy.tick() - move time in the browser', () => { + // https://on.cypress.io/tick + + // create the date in UTC so its always the same + // no matter what local timezone the browser is running in + const now = new Date(Date.UTC(2017, 2, 14)).getTime() + + cy.clock(now) + cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') + cy.get('#tick-div').click() + .should('have.text', '1489449600') + cy.tick(10000) // 10 seconds passed + cy.get('#tick-div').click() + .should('have.text', '1489449610') + }) +}) diff --git a/src/fireedge/cypress/integration/examples/traversal.spec.js b/src/fireedge/cypress/integration/examples/traversal.spec.js new file mode 100644 index 00000000000..1082eca6cc1 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/traversal.spec.js @@ -0,0 +1,121 @@ +/// + +context('Traversal', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/traversal') + }) + + it('.children() - get child DOM elements', () => { + // https://on.cypress.io/children + cy.get('.traversal-breadcrumb') + .children('.active') + .should('contain', 'Data') + }) + + it('.closest() - get closest ancestor DOM element', () => { + // https://on.cypress.io/closest + cy.get('.traversal-badge') + .closest('ul') + .should('have.class', 'list-group') + }) + + it('.eq() - get a DOM element at a specific index', () => { + // https://on.cypress.io/eq + cy.get('.traversal-list>li') + .eq(1).should('contain', 'siamese') + }) + + it('.filter() - get DOM elements that match the selector', () => { + // https://on.cypress.io/filter + cy.get('.traversal-nav>li') + .filter('.active').should('contain', 'About') + }) + + it('.find() - get descendant DOM elements of the selector', () => { + // https://on.cypress.io/find + cy.get('.traversal-pagination') + .find('li').find('a') + .should('have.length', 7) + }) + + it('.first() - get first DOM element', () => { + // https://on.cypress.io/first + cy.get('.traversal-table td') + .first().should('contain', '1') + }) + + it('.last() - get last DOM element', () => { + // https://on.cypress.io/last + cy.get('.traversal-buttons .btn') + .last().should('contain', 'Submit') + }) + + it('.next() - get next sibling DOM element', () => { + // https://on.cypress.io/next + cy.get('.traversal-ul') + .contains('apples').next().should('contain', 'oranges') + }) + + it('.nextAll() - get all next sibling DOM elements', () => { + // https://on.cypress.io/nextall + cy.get('.traversal-next-all') + .contains('oranges') + .nextAll().should('have.length', 3) + }) + + it('.nextUntil() - get next sibling DOM elements until next el', () => { + // https://on.cypress.io/nextuntil + cy.get('#veggies') + .nextUntil('#nuts').should('have.length', 3) + }) + + it('.not() - remove DOM elements from set of DOM elements', () => { + // https://on.cypress.io/not + cy.get('.traversal-disabled .btn') + .not('[disabled]').should('not.contain', 'Disabled') + }) + + it('.parent() - get parent DOM element from DOM elements', () => { + // https://on.cypress.io/parent + cy.get('.traversal-mark') + .parent().should('contain', 'Morbi leo risus') + }) + + it('.parents() - get parent DOM elements from DOM elements', () => { + // https://on.cypress.io/parents + cy.get('.traversal-cite') + .parents().should('match', 'blockquote') + }) + + it('.parentsUntil() - get parent DOM elements from DOM elements until el', () => { + // https://on.cypress.io/parentsuntil + cy.get('.clothes-nav') + .find('.active') + .parentsUntil('.clothes-nav') + .should('have.length', 2) + }) + + it('.prev() - get previous sibling DOM element', () => { + // https://on.cypress.io/prev + cy.get('.birds').find('.active') + .prev().should('contain', 'Lorikeets') + }) + + it('.prevAll() - get all previous sibling DOM elements', () => { + // https://on.cypress.io/prevAll + cy.get('.fruits-list').find('.third') + .prevAll().should('have.length', 2) + }) + + it('.prevUntil() - get all previous sibling DOM elements until el', () => { + // https://on.cypress.io/prevUntil + cy.get('.foods-list').find('#nuts') + .prevUntil('#veggies').should('have.length', 3) + }) + + it('.siblings() - get all sibling DOM elements', () => { + // https://on.cypress.io/siblings + cy.get('.traversal-pills .active') + .siblings().should('have.length', 2) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/utilities.spec.js b/src/fireedge/cypress/integration/examples/utilities.spec.js new file mode 100644 index 00000000000..51d153eeb4e --- /dev/null +++ b/src/fireedge/cypress/integration/examples/utilities.spec.js @@ -0,0 +1,133 @@ +/// + +context('Utilities', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/utilities') + }) + + it('Cypress._ - call a lodash method', () => { + // https://on.cypress.io/_ + cy.request('https://jsonplaceholder.cypress.io/users') + .then((response) => { + let ids = Cypress._.chain(response.body).map('id').take(3).value() + + expect(ids).to.deep.eq([1, 2, 3]) + }) + }) + + it('Cypress.$ - call a jQuery method', () => { + // https://on.cypress.io/$ + let $li = Cypress.$('.utility-jquery li:first') + + cy.wrap($li) + .should('not.have.class', 'active') + .click() + .should('have.class', 'active') + }) + + it('Cypress.Blob - blob utilities and base64 string conversion', () => { + // https://on.cypress.io/blob + cy.get('.utility-blob').then(($div) => + // https://github.com/nolanlawson/blob-util#imgSrcToDataURL + // get the dataUrl string for the javascript-logo + Cypress.Blob.imgSrcToDataURL('https://example.cypress.io/assets/img/javascript-logo.png', undefined, 'anonymous') + .then((dataUrl) => { + // create an element and set its src to the dataUrl + let img = Cypress.$('', { src: dataUrl }) + + // need to explicitly return cy here since we are initially returning + // the Cypress.Blob.imgSrcToDataURL promise to our test + // append the image + $div.append(img) + + cy.get('.utility-blob img').click() + .should('have.attr', 'src', dataUrl) + })) + }) + + it('Cypress.minimatch - test out glob patterns against strings', () => { + // https://on.cypress.io/minimatch + let matching = Cypress.minimatch('/users/1/comments', '/users/*/comments', { + matchBase: true, + }) + + expect(matching, 'matching wildcard').to.be.true + + matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', { + matchBase: true, + }) + expect(matching, 'comments').to.be.false + + // ** matches against all downstream path segments + matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/**', { + matchBase: true, + }) + expect(matching, 'comments').to.be.true + + // whereas * matches only the next path segment + + matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/*', { + matchBase: false, + }) + expect(matching, 'comments').to.be.false + }) + + + it('Cypress.moment() - format or parse dates using a moment method', () => { + // https://on.cypress.io/moment + const time = Cypress.moment().utc('2014-04-25T19:38:53.196Z').format('h:mm A') + + expect(time).to.be.a('string') + + cy.get('.utility-moment').contains('3:38 PM') + .should('have.class', 'badge') + + // the time in the element should be between 3pm and 5pm + const start = Cypress.moment('3:00 PM', 'LT') + const end = Cypress.moment('5:00 PM', 'LT') + + cy.get('.utility-moment .badge') + .should(($el) => { + // parse American time like "3:38 PM" + const m = Cypress.moment($el.text().trim(), 'LT') + + // display hours + minutes + AM|PM + const f = 'h:mm A' + + expect(m.isBetween(start, end), + `${m.format(f)} should be between ${start.format(f)} and ${end.format(f)}`).to.be.true + }) + }) + + + it('Cypress.Promise - instantiate a bluebird promise', () => { + // https://on.cypress.io/promise + let waited = false + + /** + * @return Bluebird + */ + function waitOneSecond () { + // return a promise that resolves after 1 second + // @ts-ignore TS2351 (new Cypress.Promise) + return new Cypress.Promise((resolve, reject) => { + setTimeout(() => { + // set waited to true + waited = true + + // resolve with 'foo' string + resolve('foo') + }, 1000) + }) + } + + cy.then(() => + // return a promise to cy.then() that + // is awaited until it resolves + // @ts-ignore TS7006 + waitOneSecond().then((str) => { + expect(str).to.eq('foo') + expect(waited).to.be.true + })) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/viewport.spec.js b/src/fireedge/cypress/integration/examples/viewport.spec.js new file mode 100644 index 00000000000..711fe74efb1 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/viewport.spec.js @@ -0,0 +1,59 @@ +/// + +context('Viewport', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/viewport') + }) + + it('cy.viewport() - set the viewport size and dimension', () => { + // https://on.cypress.io/viewport + + cy.get('#navbar').should('be.visible') + cy.viewport(320, 480) + + // the navbar should have collapse since our screen is smaller + cy.get('#navbar').should('not.be.visible') + cy.get('.navbar-toggle').should('be.visible').click() + cy.get('.nav').find('a').should('be.visible') + + // lets see what our app looks like on a super large screen + cy.viewport(2999, 2999) + + // cy.viewport() accepts a set of preset sizes + // to easily set the screen to a device's width and height + + // We added a cy.wait() between each viewport change so you can see + // the change otherwise it is a little too fast to see :) + + cy.viewport('macbook-15') + cy.wait(200) + cy.viewport('macbook-13') + cy.wait(200) + cy.viewport('macbook-11') + cy.wait(200) + cy.viewport('ipad-2') + cy.wait(200) + cy.viewport('ipad-mini') + cy.wait(200) + cy.viewport('iphone-6+') + cy.wait(200) + cy.viewport('iphone-6') + cy.wait(200) + cy.viewport('iphone-5') + cy.wait(200) + cy.viewport('iphone-4') + cy.wait(200) + cy.viewport('iphone-3') + cy.wait(200) + + // cy.viewport() accepts an orientation for all presets + // the default orientation is 'portrait' + cy.viewport('ipad-2', 'portrait') + cy.wait(200) + cy.viewport('iphone-4', 'landscape') + cy.wait(200) + + // The viewport will be reset back to the default dimensions + // in between tests (the default can be set in cypress.json) + }) +}) diff --git a/src/fireedge/cypress/integration/examples/waiting.spec.js b/src/fireedge/cypress/integration/examples/waiting.spec.js new file mode 100644 index 00000000000..e11d9ca9382 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/waiting.spec.js @@ -0,0 +1,34 @@ +/// + +context('Waiting', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/waiting') + }) + // BE CAREFUL of adding unnecessary wait times. + // https://on.cypress.io/best-practices#Unnecessary-Waiting + + // https://on.cypress.io/wait + it('cy.wait() - wait for a specific amount of time', () => { + cy.get('.wait-input1').type('Wait 1000ms after typing') + cy.wait(1000) + cy.get('.wait-input2').type('Wait 1000ms after typing') + cy.wait(1000) + cy.get('.wait-input3').type('Wait 1000ms after typing') + cy.wait(1000) + }) + + it('cy.wait() - wait for a specific route', () => { + cy.server() + + // Listen to GET to comments/1 + cy.route('GET', 'comments/*').as('getComment') + + // we have code that gets a comment when + // the button is clicked in scripts.js + cy.get('.network-btn').click() + + // wait for GET comments/1 + cy.wait('@getComment').its('status').should('eq', 200) + }) + +}) diff --git a/src/fireedge/cypress/integration/examples/window.spec.js b/src/fireedge/cypress/integration/examples/window.spec.js new file mode 100644 index 00000000000..00bff9f7fa9 --- /dev/null +++ b/src/fireedge/cypress/integration/examples/window.spec.js @@ -0,0 +1,22 @@ +/// + +context('Window', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/window') + }) + + it('cy.window() - get the global window object', () => { + // https://on.cypress.io/window + cy.window().should('have.property', 'top') + }) + + it('cy.document() - get the document object', () => { + // https://on.cypress.io/document + cy.document().should('have.property', 'charset').and('eq', 'UTF-8') + }) + + it('cy.title() - get the title', () => { + // https://on.cypress.io/title + cy.title().should('include', 'Kitchen Sink') + }) +}) diff --git a/src/fireedge/cypress/support/commands.js b/src/fireedge/cypress/support/commands.js new file mode 100644 index 00000000000..c1f5a772e2b --- /dev/null +++ b/src/fireedge/cypress/support/commands.js @@ -0,0 +1,25 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This is will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/src/fireedge/cypress/support/index.js b/src/fireedge/cypress/support/index.js new file mode 100644 index 00000000000..d68db96df26 --- /dev/null +++ b/src/fireedge/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/src/fireedge/src/index.js b/src/fireedge/src/index.js index 9cf801462e3..c7ba835a335 100644 --- a/src/fireedge/src/index.js +++ b/src/fireedge/src/index.js @@ -30,22 +30,22 @@ const { const { createServer: unsecureServer } = require('http'); const { createServer: secureServer } = require('https'); const bodyParser = require('body-parser'); -const { getConfig } = require('./utils/yml-connect'); const { defaultConfigLogPath, defaultConfigLogFile, defaultTypeLog -} = require('./config/defaults'); -const publicRoutes = require('./routes/public'); -const apiRoutes = require('./routes/api'); -const { messageTerminal, addWsServer } = require('./utils'); +} = require('./utils/contants'); +const { + entrypoint404, + entrypointApi, + entrypointApp +} = require('./routes/entrypoints'); +const { messageTerminal, addWsServer, getConfig } = require('./utils'); const app = express(); -// user config -const appConfig = getConfig(); - // settings +const appConfig = getConfig(); const port = appConfig.PORT || 3000; const userLog = appConfig.LOG || 'dev'; @@ -88,19 +88,13 @@ app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); // routes -app.use('/api', apiRoutes); -app.use('/', publicRoutes); +app.use('/api', entrypointApi); // opennebula Api routes +app.use('/', entrypointApp); // html for react app frontend // 404 - public -app.get('*', (req, res) => - res - .status(404) - .send( - `404 - Not Found` - ) -); +app.get('*', entrypoint404); -// server +// server certificates const appServer = existsSync && key && cert && existsSync(key) && existsSync(cert) ? secureServer( diff --git a/src/fireedge/src/public/app.js b/src/fireedge/src/public/app.js index 46e467f288c..8a48831178e 100644 --- a/src/fireedge/src/public/app.js +++ b/src/fireedge/src/public/app.js @@ -17,7 +17,7 @@ import React from 'react'; import { StaticRouter, BrowserRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import PropTypes from 'prop-types'; -import { TranslateProvider } from './components/HOC'; +import { TranslateProvider } from 'client/components/HOC'; import Router from './components/router'; const App = ({ location, context, store }) => ( diff --git a/src/fireedge/src/public/components/FormControl/inputCode.js b/src/fireedge/src/public/components/FormControl/inputCode.js index 6afef914834..eb964b52983 100644 --- a/src/fireedge/src/public/components/FormControl/inputCode.js +++ b/src/fireedge/src/public/components/FormControl/inputCode.js @@ -1,17 +1,19 @@ -import React from 'react' -import { Box } from '@material-ui/core' -import AceEditor from 'react-ace' +import React from 'react'; +import { Box } from '@material-ui/core'; +import AceEditor from 'react-ace'; +import PropTypes from 'prop-types'; +import 'ace-builds/src-noconflict/mode-json'; +import 'ace-builds/src-noconflict/theme-github'; -import 'ace-builds/src-noconflict/mode-json' -import 'ace-builds/src-noconflict/theme-github' +const { string } = PropTypes; -export default function InputCode({ code = '', language = 'json', ...props }) { +const InputCode = ({ code, language, ...props }) => { const handleChange = newValue => { - console.log("change", newValue); - } + console.log('change', newValue); + }; return ( - + - ) -} + ); +}; + +InputCode.propTypes = { + code: string, + language: string +}; + +InputCode.defaultProps = { + code: '', + language: 'json' +}; + +export default InputCode; diff --git a/src/fireedge/src/public/components/HOC/Translate.js b/src/fireedge/src/public/components/HOC/Translate.js index d44cd933a7c..7650a10d440 100644 --- a/src/fireedge/src/public/components/HOC/Translate.js +++ b/src/fireedge/src/public/components/HOC/Translate.js @@ -18,7 +18,7 @@ import PropTypes from 'prop-types'; import { Select } from '@material-ui/core'; import { sprintf } from 'sprintf-js'; import root from 'window-or-global'; -import { translations, defaultLang } from '../../../config/defaults'; +import { translations, defaultLang } from '../../../utils/contants/defaults'; const langDefault = defaultLang; const TranslateContext = new CreateContext(); diff --git a/src/fireedge/src/public/components/containers/TestApi/ResponseForm.js b/src/fireedge/src/public/components/containers/TestApi/ResponseForm.js new file mode 100644 index 00000000000..93843ac1aa7 --- /dev/null +++ b/src/fireedge/src/public/components/containers/TestApi/ResponseForm.js @@ -0,0 +1,137 @@ +import React from 'react'; +import { object, string, shape, func } from 'prop-types'; + +import { useForm, Controller } from 'react-hook-form'; +import { + TextField, + Grid, + Typography, + Box, + FormControlLabel, + Checkbox +} from '@material-ui/core'; + +import ButtonSubmit from '../../FormControl/submitButton'; +import { requestData } from '../../../utils'; +import { from as resourceFrom } from '../../../../utils/contants/defaults'; + +const getQueries = params => + Object.entries(params) + ?.filter(([, { from }]) => from === resourceFrom.query) + ?.map(([name, { default: value }]) => `${name}=${encodeURI(value)}`) + ?.join('&'); + +const getResources = params => + Object.values(params) + ?.filter(({ from }) => from === resourceFrom.resource) + ?.map(({ default: value }) => value) + ?.join('/'); + +const getDataBody = params => + Object.entries(params) + ?.filter(([, { from }]) => from === resourceFrom.postBody) + ?.reduce( + (acc, [name, { default: value }]) => ({ ...acc, [name]: value }), + {} + ); + +const ResponseForm = ({ + handleChangeResponse, + command: { name, httpMethod, params } +}) => { + const { control, handleSubmit, errors, formState } = useForm(); + + const onSubmit = dataForm => { + /* Spread 'from' values in current params */ + const reqParams = Object.entries(params)?.reduce( + (acc, [name, { from }]) => ({ + ...acc, + [name]: { from, ...dataForm[name] } + }), + {} + ); + + const queries = getQueries(reqParams); + const resources = getResources(reqParams); + const data = getDataBody(reqParams); + + const url = `api/${name.replace('.', '/')}`; + + requestData(`${url}/${resources}?${queries}`, { + data, + method: httpMethod, + authenticate: true + }).then(({ id, ...res }) => { + id === 401 && console.log('ERROR'); + id === 200 && handleChangeResponse(JSON.stringify(res, null, '\t')); + }); + }; + + return ( + + + {name || 'Request'} + + + {Object.entries(params)?.map(([nameCommand, { default: value }]) => ( + + } + label={nameCommand} + labelPlacement={nameCommand} + /> + ) : ( + + ) + } + control={control} + name={`${nameCommand}.default`} + defaultValue={value} + /> + + ))} + + + + + + ); +}; + +ResponseForm.propTypes = { + command: shape({ + name: string.isRequired, + httpMethod: string.isRequired, + schema: object, + params: object + }).isRequired, + handleChangeResponse: func +}; + +ResponseForm.defaultProps = { + command: { + name: '', + httpMethod: 'GET', + schema: {}, + params: {} + }, + handleChangeResponse: () => undefined +}; +export default ResponseForm; diff --git a/src/fireedge/src/public/components/containers/TestApi/form.js b/src/fireedge/src/public/components/containers/TestApi/form.js deleted file mode 100644 index 9708e5150b2..00000000000 --- a/src/fireedge/src/public/components/containers/TestApi/form.js +++ /dev/null @@ -1,121 +0,0 @@ -import React from 'react' -import { object, string, shape } from 'prop-types' - -import { useForm, Controller } from 'react-hook-form' -import { - TextField, Grid, Typography, Box, FormControlLabel, Checkbox -} from '@material-ui/core' - -import ButtonSubmit from '../../FormControl/submitButton' -import { requestData } from '../../../utils' -import * as DEFAULTS from '../../../../config/defaults' - - -const getQueries = params => Object.entries(params) - ?.filter(([, { from }]) => from === DEFAULTS.from.query) - ?.map(([name, { default: value }]) => `${name}=${encodeURI(value)}`) - ?.join('&') - -const getResources = params => Object.values(params) - ?.filter(({ from }) => from === DEFAULTS.from.resource) - ?.map(({ default: value }) => value) - ?.join('/') - -const getDataBody = params => Object.entries(params) - ?.filter(([, { from }]) => from === DEFAULTS.from.postBody) - ?.reduce((acc, [name, { default: value }]) => ( - { ...acc, [name]: value } - ), {}) - - -export default function ResponseForm({ - handleChangeResponse, - command: { name, httpMethod, params } -}) { - const { control, handleSubmit, errors, formState } = useForm() - - const onSubmit = dataForm => { - /* Spread 'from' values in current params */ - const reqParams = Object.entries(params) - ?.reduce((acc, [name, { from }]) => ({ - ...acc, [name]: { from, ...dataForm[name] } - }), {}) - - const queries = getQueries(reqParams) - const resources = getResources(reqParams) - const data = getDataBody(reqParams) - - const url = `api/${name.replace('.', '/')}` - - requestData( - `${url}/${resources}?${queries}`, - { data, method: httpMethod, authenticate: true } - ).then(({ id, ...res }) => { - id === 401 && console.log('ERROR') - id === 200 && handleChangeResponse(JSON.stringify(res, null, '\t')) - }) - } - - return ( - - - {name || 'Request'} - - - {Object.entries(params)?.map(([name, { default: value }]) => - - } - label={name} - labelPlacement={name} - /> - : - } - control={control} - name={`${name}.default`} - defaultValue={value} - /> - - )} - - - - - - ) -} - -ResponseForm.propTypes = { - command: shape({ - name: string.isRequired, - httpMethod: string.isRequired, - schema: object, - params: object - }).isRequired -} - -ResponseForm.defaultProps = { - command: { - name: '', - httpMethod: 'GET', - schema: {}, - params: {} - } -} \ No newline at end of file diff --git a/src/fireedge/src/public/components/containers/TestApi/index.js b/src/fireedge/src/public/components/containers/TestApi/index.js index 314300d34ef..324d04d3cc8 100644 --- a/src/fireedge/src/public/components/containers/TestApi/index.js +++ b/src/fireedge/src/public/components/containers/TestApi/index.js @@ -15,18 +15,17 @@ import React, { useState, useMemo } from 'react'; import { TextField, Grid, MenuItem } from '@material-ui/core'; -import ResponseForm from './form'; -import Commands from '../../../../config/commands-params' -import { Translate } from '../../HOC'; +import Commands from '../../../../utils/contants/commands'; +import { Translate } from '../../../components/HOC'; +import ResponseForm from './ResponseForm'; import InputCode from '../../FormControl/inputCode'; -export default function TestApi() { - const [name, setName] = useState('acl.addrule') - const [response, setResponse] = useState('') - - const handleChangeCommand = evt => setName(evt?.target?.value) - const handleChangeResponse = res => setResponse(res) +const TestApi = () => { + const [name, setName] = useState('acl.addrule'); + const [response, setResponse] = useState(''); + const handleChangeCommand = evt => setName(evt?.target?.value); + const handleChangeResponse = res => setResponse(res); return ( @@ -41,22 +40,32 @@ export default function TestApi() { - { useMemo(() => Object.keys(Commands)?.map(name => ( - - {name} - - ), [])) - } + {useMemo(() => + Object.keys(Commands)?.map( + commandName => ( + + {commandName} + + ), + [] + ) + )} - { name && name !== '' && ( + {name && name !== '' && ( + command={{ name, ...Commands[name] }} + /> )} - ) -} + ); +}; + +export default TestApi; diff --git a/src/fireedge/src/public/components/containers/Websocket/WSConsole.js b/src/fireedge/src/public/components/containers/Websocket/WSConsole.js index 6d3c7c5d54a..c21d446fb87 100644 --- a/src/fireedge/src/public/components/containers/Websocket/WSConsole.js +++ b/src/fireedge/src/public/components/containers/Websocket/WSConsole.js @@ -16,7 +16,7 @@ import React, { Component } from 'react'; import { w3cwebsocket as W3CWebSocket } from 'websocket'; import JSONPretty from 'react-json-pretty'; -import { messageTerminal } from '../../../../utils/general-functions'; +import { messageTerminal } from '../../../../utils/general'; class WSConsole extends Component { constructor(props) { diff --git a/src/fireedge/src/public/front-app.js b/src/fireedge/src/public/front-app.js index f054eef5afd..03839491e5e 100644 --- a/src/fireedge/src/public/front-app.js +++ b/src/fireedge/src/public/front-app.js @@ -17,8 +17,8 @@ import React from 'react'; import { hydrate } from 'react-dom'; import { createStore } from 'redux'; import root from 'window-or-global'; -import rootReducer from '../public/reducers'; -import App from './app'; +import rootReducer from 'client/reducers'; +import App from 'client/app'; const preloadedState = root.__PRELOADED_STATE__; diff --git a/src/fireedge/src/public/utils/utils.js b/src/fireedge/src/public/utils/utils.js index 91c255e0dae..cad255b466f 100644 --- a/src/fireedge/src/public/utils/utils.js +++ b/src/fireedge/src/public/utils/utils.js @@ -16,7 +16,7 @@ import axios from 'axios'; import root from 'window-or-global'; import constants from '../constants'; -import { messageTerminal } from '../../utils/general-functions'; +import { messageTerminal } from '../../utils/general'; export const storage = (name = '', data = '', keepData = false) => { if (name && data && root && root.localStorage && root.sessionStorage) { diff --git a/src/fireedge/src/routes/api/index.js b/src/fireedge/src/routes/api/index.js new file mode 100644 index 00000000000..6bffe8b6625 --- /dev/null +++ b/src/fireedge/src/routes/api/index.js @@ -0,0 +1,63 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const { getRouteForOpennebulaCommand } = require('../../utils/opennebula'); +const { + private: functions2faPrivate, + public: functions2faPublic +} = require('./routes/2fa'); +const { + private: functionsAuthPrivate, + public: functionsAuthPublic +} = require('./routes/auth'); +const { + private: functionsOneflowPrivate, + public: functionsOneflowPublic +} = require('./routes/oneflow'); +const { + private: functionsSupportPrivate, + public: functionsSupportPublic +} = require('./routes/support'); +const { + private: functionsVcenterPrivate, + public: functionsVcenterPublic +} = require('./routes/vcenter'); +const { + private: functionsZendeskPrivate, + public: functionsZendeskPublic +} = require('./routes/zendesk'); + +const opennebulaActions = getRouteForOpennebulaCommand(); + +const routes = { + private: { + ...opennebulaActions, + ...functions2faPrivate, + ...functionsAuthPrivate, + ...functionsOneflowPrivate, + ...functionsSupportPrivate, + ...functionsVcenterPrivate, + ...functionsZendeskPrivate + }, + public: { + ...functions2faPublic, + ...functionsAuthPublic, + ...functionsOneflowPublic, + ...functionsSupportPublic, + ...functionsVcenterPublic, + ...functionsZendeskPublic + } +}; +module.exports = routes; diff --git a/src/fireedge/src/config/function-routes.js b/src/fireedge/src/routes/api/routes/2fa/index.js similarity index 57% rename from src/fireedge/src/config/function-routes.js rename to src/fireedge/src/routes/api/routes/2fa/index.js index 3c6040affd0..0061c34e687 100644 --- a/src/fireedge/src/config/function-routes.js +++ b/src/fireedge/src/routes/api/routes/2fa/index.js @@ -14,40 +14,32 @@ /* -------------------------------------------------------------------------- */ const { Map } = require('immutable'); -const { getConfig } = require('../utils/yml-connect'); -const moment = require('moment'); const speakeasy = require('speakeasy'); const qrcode = require('qrcode'); - +const { getConfig } = require('../../../../utils/yml'); const { httpMethod, - defaultNamespace, - defaultMethodLogin, defaultMethodUserInfo, defaultMethodUserUpdate, default2FAIssuer, default2FAOpennebulaVar, default2FAOpennebulaTmpVar -} = require('./defaults'); +} = require('../../../../utils/contants/defaults'); const { ok, unauthorized, internalServerError -} = require('../config/http-codes'); -const { from: fromData } = require('../config/defaults'); -const { createToken } = require('../utils/jwt-functions'); +} = require('../../../../utils/contants/http-codes'); +const { from: fromData } = require('../../../../utils/contants/defaults'); const { responseOpennebula, checkOpennebulaCommand, - paramsDefaultByCommandOpennebula, generateNewTemplate -} = require('../utils/opennebula-functions'); +} = require('../../../../utils/opennebula'); // user config const appConfig = getConfig(); -const limitToken = appConfig.LIMIT_TOKEN; -const namespace = appConfig.NAMESPACE || defaultNamespace; const twoFactorAuthIssuer = appConfig.TWO_FACTOR_AUTH_ISSUER || default2FAIssuer; @@ -298,216 +290,7 @@ const privateRoutes = { } }; -const publicRoutes = { - auth: { - httpMethod: POST, - action: (req, res, next, connect) => { - const updaterResponse = code => { - if ( - 'id' in code && - 'message' in code && - res && - res.locals && - res.locals.httpCode - ) { - res.locals.httpCode = code; - } - }; - updaterResponse(Map(internalServerError).toObject()); - - const getOpennebulaMethod = checkOpennebulaCommand( - defaultMethodLogin, - POST - ); - - if (req && getOpennebulaMethod) { - const user = - (req && - fromData.postBody && - req[fromData.postBody] && - req[fromData.postBody].user) || - ''; - - const pass = - (req && - fromData.postBody && - req[fromData.postBody] && - req[fromData.postBody].pass) || - ''; - - const token = - (req && req[fromData.postBody] && req[fromData.postBody].token) || ''; - - const extended = - (req && req[fromData.postBody] && req[fromData.postBody].extended) || - ''; - - if (user && pass && connect && limitToken) { - const { MIN, MAX } = limitToken; - - const now = moment(); - const nowUnix = now.unix(); - const nowWithDays = moment().add(extended ? MAX : MIN, 'days'); - const relativeTime = nowWithDays.diff(now, 'seconds'); - - let opennebulaToken; - const connectOpennebula = connect( - user, - pass - ); - const dataSourceWithExpirateDate = Map(req).toObject(); - - const userInfo = userData => { - if (user && opennebulaToken && userData && userData.USER) { - const informationUser = userData.USER; - - // remove opennebula user tokens - if ( - informationUser.LOGIN_TOKEN && - Array.isArray(informationUser.LOGIN_TOKEN) - ) { - informationUser.LOGIN_TOKEN.map(loginToken => { - if ( - loginToken && - loginToken.TOKEN && - loginToken.TOKEN !== opennebulaToken - ) { - dataSourceWithExpirateDate[fromData.postBody].expire = 0; - dataSourceWithExpirateDate[fromData.postBody].token = - loginToken.TOKEN; - - connectOpennebula( - defaultMethodLogin, - getOpennebulaMethod(dataSourceWithExpirateDate), - (err, value) => { - responseOpennebula( - () => undefined, - err, - value, - () => undefined, - next - ); - } - ); - } - }); - } - - // validate 2fa token - if ( - informationUser.TEMPLATE && - informationUser.TEMPLATE.SUNSTONE && - informationUser.TEMPLATE.SUNSTONE[default2FAOpennebulaVar] - ) { - const secret = - informationUser.TEMPLATE.SUNSTONE[default2FAOpennebulaVar]; - const verified = speakeasy.totp.verify({ - secret, - encoding: 'base32', - token - }); - if (!verified) { - const codeUnauthorized = Map(unauthorized).toObject(); - codeUnauthorized.data = { message: 'invalid 2fa token' }; - updaterResponse(codeUnauthorized); - next(); - } - } - - // generate jwt - const { ID: id } = informationUser; - const dataJWT = { id, user, token: opennebulaToken }; - const jwt = createToken( - dataJWT, - nowUnix, - nowWithDays.format('X') - ); - if (jwt) { - const codeOK = Map(ok).toObject(); - codeOK.data = { token: jwt }; - updaterResponse(codeOK); - } - next(); - } else { - next(); - } - }; - - const authenticated = val => { - const findTextError = `[${namespace + defaultMethodLogin}]`; - if (val) { - if (val.indexOf(findTextError) >= 0) { - const codeUnauthorized = Map(unauthorized).toObject(); - updaterResponse(codeUnauthorized); - next(); - } else { - opennebulaToken = val; - connectOpennebula( - defaultMethodUserInfo, - paramsDefaultByCommandOpennebula(defaultMethodUserInfo, GET), - (err, value) => { - responseOpennebula( - updaterResponse, - err, - value, - userInfo, - next - ); - } - ); - } - } else { - next(); - } - }; - - // add expire time unix for opennebula creation token - dataSourceWithExpirateDate[fromData.postBody].expire = relativeTime; - - connectOpennebula( - defaultMethodLogin, - getOpennebulaMethod(dataSourceWithExpirateDate), - (err, value) => { - responseOpennebula( - updaterResponse, - err, - value, - authenticated, - next - ); - } - ); - } else { - res.locals.httpCode = Map(unauthorized).toObject(); - next(); - } - } else { - next(); - } - } - }, - zendesk: { - httpMethod: POST, - action: (req, res, next) => { - next(); - console.log('zendesk'); - } - }, - latest: { - httpMethod: POST, - action: (req, res, next) => { - next(); - console.log('latest version opennebula'); - } - }, - support: { - httpMethod: POST, - action: (req, res, next) => { - next(); - console.log('support token'); - } - } -}; +const publicRoutes = {}; const functionRoutes = { private: privateRoutes, diff --git a/src/fireedge/src/routes/api/routes/auth/functions.js b/src/fireedge/src/routes/api/routes/auth/functions.js new file mode 100644 index 00000000000..8ce596ff144 --- /dev/null +++ b/src/fireedge/src/routes/api/routes/auth/functions.js @@ -0,0 +1,258 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ +const speakeasy = require('speakeasy'); +const moment = require('moment'); +const { Map } = require('immutable'); +const { + httpMethod, + defaultMethodLogin, + defaultMethodUserInfo, + default2FAOpennebulaVar, + defaultNamespace, + from: fromData +} = require('../../../../utils/contants/defaults'); +const { getConfig } = require('../../../../utils/yml'); +const { ok, unauthorized } = require('../../../../utils/contants/http-codes'); +const { createToken } = require('../../../../utils/jwt'); +const { + responseOpennebula, + paramsDefaultByCommandOpennebula, + checkOpennebulaCommand +} = require('../../../../utils/opennebula'); + +const { zones, oneConfig } = global; + +const appConfig = getConfig(); + +const namespace = appConfig.NAMESPACE || defaultNamespace; +const { GET, POST } = httpMethod; + +const getOpennebulaMethod = checkOpennebulaCommand(defaultMethodLogin, POST); + +let opennebulaToken = ''; +let user = ''; +let pass = ''; +let tfatoken = ''; +let extended = ''; +let next = () => undefined; +let req = {}; +let res = {}; +let nodeConnect = () => undefined; +let now = ''; +let nowUnix = ''; +let nowWithDays = ''; +let relativeTime = ''; + +const dataSourceWithExpirateDate = () => Map(req).toObject(); + +const getUser = () => user; +const getPass = () => pass; +const getRelativeTime = () => relativeTime; + +const setUser = newUser => { + user = newUser; + return user; +}; + +const setPass = newPass => { + pass = newPass; + return pass; +}; + +const setTfaToken = newTfaToken => { + tfatoken = newTfaToken; + return tfatoken; +}; + +const setExtended = newExtended => { + extended = newExtended; + return extended; +}; +const setNext = newNext => { + next = newNext; + return next; +}; +const setReq = newReq => { + req = newReq; + return req; +}; +const setNodeConnect = newConnect => { + nodeConnect = newConnect; + return nodeConnect; +}; + +const setRes = newRes => { + res = newRes; + return res; +}; + +const setDates = () => { + const limitToken = appConfig.LIMIT_TOKEN; + const { MIN, MAX } = limitToken; + now = moment(); + nowUnix = now.unix(); + nowWithDays = moment().add(extended ? MAX : MIN, 'days'); + relativeTime = nowWithDays.diff(now, 'seconds'); +}; + +const connectOpennebula = () => nodeConnect(user, pass); + +const updaterResponse = code => { + if ( + 'id' in code && + 'message' in code && + res && + res.locals && + res.locals.httpCode + ) { + res.locals.httpCode = code; + } +}; + +const validate2faAuthentication = informationUser => { + if ( + informationUser.TEMPLATE && + informationUser.TEMPLATE.SUNSTONE && + informationUser.TEMPLATE.SUNSTONE[default2FAOpennebulaVar] + ) { + const secret = informationUser.TEMPLATE.SUNSTONE[default2FAOpennebulaVar]; + const verified = speakeasy.totp.verify({ + secret, + encoding: 'base32', + tfatoken + }); + if (!verified) { + const codeUnauthorized = Map(unauthorized).toObject(); + codeUnauthorized.data = { message: 'invalid 2fa token' }; + updaterResponse(codeUnauthorized); + next(); + } + } +}; + +const genJWT = informationUser => { + if (informationUser && informationUser.ID) { + const { ID: id } = informationUser; + const dataJWT = { id, user, token: opennebulaToken }; + const jwt = createToken(dataJWT, nowUnix, nowWithDays.format('X')); + if (jwt) { + const codeOK = Map(ok).toObject(); + codeOK.data = { token: jwt }; + updaterResponse(codeOK); + } + } +}; + +const setZones = () => { + if (!zones) { + console.log('setZones'); + } +}; + +const setOneConfig = () => { + if (!oneConfig) { + console.log('setConfig'); + } +}; + +const userInfo = userData => { + if (user && opennebulaToken && userData && userData.USER) { + const informationUser = userData.USER; + // remove opennebula user tokens + if ( + informationUser.LOGIN_TOKEN && + Array.isArray(informationUser.LOGIN_TOKEN) + ) { + informationUser.LOGIN_TOKEN.forEach(loginToken => { + if ( + loginToken && + loginToken.TOKEN && + loginToken.TOKEN !== opennebulaToken + ) { + const dataSource = dataSourceWithExpirateDate(); + dataSource[fromData.postBody].expire = 0; + dataSource[fromData.postBody].token = loginToken.TOKEN; + const oneConnect = connectOpennebula(); + oneConnect( + defaultMethodLogin, + getOpennebulaMethod(dataSource), + (err, value) => { + // res, err, value, response, next + responseOpennebula( + () => undefined, + err, + value, + () => { + setZones(); + setOneConfig(); + // aca se tiene que hacer la llamada a las zonas y a system.config + }, + next + ); + } + ); + } + }); + } + validate2faAuthentication(informationUser); + genJWT(informationUser); + next(); + } else { + next(); + } +}; + +const authenticate = val => { + const findTextError = `[${namespace + defaultMethodLogin}]`; + if (val) { + if (val.indexOf(findTextError) >= 0) { + const codeUnauthorized = Map(unauthorized).toObject(); + updaterResponse(codeUnauthorized); + next(); + } else { + const oneConnect = connectOpennebula(); + opennebulaToken = val; + oneConnect( + defaultMethodUserInfo, + paramsDefaultByCommandOpennebula(defaultMethodUserInfo, GET), + (err, value) => { + responseOpennebula(updaterResponse, err, value, userInfo, next); + } + ); + } + } else { + next(); + } +}; + +const functionRoutes = { + authenticate, + getUser, + getPass, + setUser, + setPass, + setTfaToken, + setExtended, + setNext, + setReq, + setRes, + updaterResponse, + setNodeConnect, + connectOpennebula, + setDates, + getRelativeTime +}; + +module.exports = functionRoutes; diff --git a/src/fireedge/src/routes/api/routes/auth/index.js b/src/fireedge/src/routes/api/routes/auth/index.js new file mode 100644 index 00000000000..61b973624a2 --- /dev/null +++ b/src/fireedge/src/routes/api/routes/auth/index.js @@ -0,0 +1,129 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const { Map } = require('immutable'); +const { + httpMethod, + defaultMethodLogin +} = require('../../../../utils/contants/defaults'); +const { + unauthorized, + internalServerError +} = require('../../../../utils/contants/http-codes'); +const { from: fromData } = require('../../../../utils/contants/defaults'); + +const { + responseOpennebula, + checkOpennebulaCommand +} = require('../../../../utils/opennebula'); + +const { + authenticate, + getUser, + getPass, + setUser, + setPass, + setTfaToken, + setExtended, + setNext, + setReq, + setRes, + setNodeConnect, + setDates, + getRelativeTime, + connectOpennebula, + updaterResponse +} = require('./functions'); + +const { POST } = httpMethod; + +const privateRoutes = {}; + +const publicRoutes = { + auth: { + httpMethod: POST, + action: (req, res, next, connect) => { + if (req && res && connect) { + setReq(req); + setRes(res); + setNext(next); + updaterResponse(Map(internalServerError).toObject()); + const getOpennebulaMethod = checkOpennebulaCommand( + defaultMethodLogin, + POST + ); + if (getOpennebulaMethod) { + setUser( + (req && + fromData.postBody && + req[fromData.postBody] && + req[fromData.postBody].user) || + '' + ); + setPass( + (req && + fromData.postBody && + req[fromData.postBody] && + req[fromData.postBody].pass) || + '' + ); + setTfaToken( + (req && req[fromData.postBody] && req[fromData.postBody].token) || + '' + ); + setExtended( + (req && + req[fromData.postBody] && + req[fromData.postBody].extended) || + '' + ); + setNodeConnect(connect); + if (getUser() && getPass()) { + setDates(); + const relativeTime = getRelativeTime(); + const oneConnect = connectOpennebula(); + const dataSourceWithExpirateDate = Map(req).toObject(); + // add expire time unix for opennebula creation token + dataSourceWithExpirateDate[fromData.postBody].expire = relativeTime; + oneConnect( + defaultMethodLogin, + getOpennebulaMethod(dataSourceWithExpirateDate), + (err, value) => { + responseOpennebula( + updaterResponse, + err, + value, + authenticate, + next + ); + } + ); + } + } else { + next(); + } + } else { + next(); + } + } + } +}; + +const functionRoutes = { + private: privateRoutes, + public: publicRoutes +}; + +module.exports = functionRoutes; diff --git a/src/fireedge/src/config/routes-api.js b/src/fireedge/src/routes/api/routes/oneflow/index.js similarity index 75% rename from src/fireedge/src/config/routes-api.js rename to src/fireedge/src/routes/api/routes/oneflow/index.js index 3aca1c0c713..fe4ef3e550b 100644 --- a/src/fireedge/src/config/routes-api.js +++ b/src/fireedge/src/routes/api/routes/oneflow/index.js @@ -13,16 +13,13 @@ /* limitations under the License. */ /* -------------------------------------------------------------------------- */ -const { getRouteForOpennebulaCommand } = require('../utils'); -const { - private: functionPrivate, - public: functionPublic -} = require('./function-routes'); +const privateRoutes = {}; -const opennebulaActions = getRouteForOpennebulaCommand(); +const publicRoutes = {}; -const routes = { - private: [...opennebulaActions, ...Object.keys(functionPrivate)], - public: [...Object.keys(functionPublic)] +const functionRoutes = { + private: privateRoutes, + public: publicRoutes }; -module.exports = routes; + +module.exports = functionRoutes; diff --git a/src/fireedge/src/routes/api/routes/support/index.js b/src/fireedge/src/routes/api/routes/support/index.js new file mode 100644 index 00000000000..fe4ef3e550b --- /dev/null +++ b/src/fireedge/src/routes/api/routes/support/index.js @@ -0,0 +1,25 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const privateRoutes = {}; + +const publicRoutes = {}; + +const functionRoutes = { + private: privateRoutes, + public: publicRoutes +}; + +module.exports = functionRoutes; diff --git a/src/fireedge/src/routes/api/routes/vcenter/index.js b/src/fireedge/src/routes/api/routes/vcenter/index.js new file mode 100644 index 00000000000..fe4ef3e550b --- /dev/null +++ b/src/fireedge/src/routes/api/routes/vcenter/index.js @@ -0,0 +1,25 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const privateRoutes = {}; + +const publicRoutes = {}; + +const functionRoutes = { + private: privateRoutes, + public: publicRoutes +}; + +module.exports = functionRoutes; diff --git a/src/fireedge/src/routes/api/routes/zendesk/index.js b/src/fireedge/src/routes/api/routes/zendesk/index.js new file mode 100644 index 00000000000..fe4ef3e550b --- /dev/null +++ b/src/fireedge/src/routes/api/routes/zendesk/index.js @@ -0,0 +1,25 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const privateRoutes = {}; + +const publicRoutes = {}; + +const functionRoutes = { + private: privateRoutes, + public: publicRoutes +}; + +module.exports = functionRoutes; diff --git a/src/fireedge/src/routes/entrypoints/404.js b/src/fireedge/src/routes/entrypoints/404.js new file mode 100644 index 00000000000..f8a42af18d6 --- /dev/null +++ b/src/fireedge/src/routes/entrypoints/404.js @@ -0,0 +1,28 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const express = require('express'); + +const router = express.Router(); + +router.get('*', (req, res) => + res + .status(404) + .send( + `404 - Not Found` + ) +); + +module.exports = router; diff --git a/src/fireedge/src/routes/api.js b/src/fireedge/src/routes/entrypoints/Api.js~HEAD similarity index 56% rename from src/fireedge/src/routes/api.js rename to src/fireedge/src/routes/entrypoints/Api.js~HEAD index 4a9fedf0401..dc022dd06e5 100644 --- a/src/fireedge/src/routes/api.js +++ b/src/fireedge/src/routes/entrypoints/Api.js~HEAD @@ -15,150 +15,56 @@ const express = require('express'); const { Map } = require('immutable'); -const { - private: authenticated, - public: nonAuthenticated -} = require('../config/routes-api'); -const params = require('../config/params'); -const { getConfig } = require('../utils/yml-connect'); +const { defaults, httpCodes, params } = require('../../utils/contants'); +const { getConfig } = require('../../utils/yml'); const { opennebulaConnect, - validateAuth, - createParamsState, - createQueriesState, checkRouteFunction, commandXML, checkOpennebulaCommand, - getAllowedQueryParams, validateRouteFunction, responseOpennebula -} = require('../utils'); +} = require('../../utils'); + +const { + validateResource, + optionalParameters, + optionalQueries, + clearStates, + getParamsState, + getQueriesState, + getIdUserOpennebula, + getUserOpennebula, + getPassOpennebula +} = require('./middlewares'); + const { defaultOpennebulaZones, defaultMessageInvalidZone, defaultGetMethod, - httpMethod: httpMethods -} = require('../config/defaults'); - -const httpCodes = require('../config/http-codes'); -const { from: fromData } = require('../config/defaults'); + httpMethod: httpMethods, + from: fromData +} = defaults; const router = express.Router(); express(); -const defaultParams = Map(createParamsState()); -const defaultQueries = Map(createQueriesState()); - -let paramsState = defaultParams.toObject(); -let queriesState = defaultQueries.toObject(); -let idUserOpennebula = ''; -let userOpennebula = ''; -let passOpennebula = ''; - // user config const appConfig = getConfig(); const opennebulaZones = appConfig.OPENNEBULA_ZONES || defaultOpennebulaZones; -const clearStates = () => { - paramsState = defaultParams.toObject(); - queriesState = defaultQueries.toObject(); - idUserOpennebula = ''; - userOpennebula = ''; - passOpennebula = ''; -}; - const paramsToRoutes = () => Object.keys(params).reduce( (resources, param) => String(resources).concat(`/:${params[param]}?`), '/:resource?' ); -const validateResource = (req, res, next) => { - const { badRequest, unauthorized, serviceUnavailable } = httpCodes; - let status = badRequest; - if (req && req.params && req.params.resource) { - const resource = req.params.resource; - status = serviceUnavailable; - if (authenticated.includes(resource)) { - const session = validateAuth(req); - if ( - session && - 'iss' in session && - 'aud' in session && - 'jti' in session && - 'iat' in session && - 'exp' in session - ) { - idUserOpennebula = session.iss; - userOpennebula = session.aud; - passOpennebula = session.jti; - next(); - return; - } - status = unauthorized; - } - if (nonAuthenticated.includes(resource)) { - next(); - return; - } - } - res.status(status.id).json(status); -}; - -const optionalParameters = (req, res, next) => { - if (req && req.params) { - let start = true; - const keys = Object.keys(req.params); - keys.map(param => { - if (start) { - start = false; - return start; - } - if (req.params[param]) { - const matches = req.params[param].match(/(^[\w]*=)/gi); - if (matches && matches[0]) { - params.map(parameter => { - if ( - matches[0].replace(/=/g, '').toLowerCase() === - parameter.toLowerCase() - ) { - const removeKey = new RegExp(`^${parameter}=`, 'i'); - if (paramsState[parameter] === null) { - paramsState[parameter] = req.params[param].replace( - removeKey, - '' - ); - } - } - }); - } else { - paramsState[param] = req.params[param]; - } - } - }); - } - next(); -}; - -const optionalQueries = (req, res, next) => { - if (req && req.query) { - const keys = Object.keys(req.query); - const queries = getAllowedQueryParams(); - keys.map(query => { - if (req.query[query] && queries.includes(query)) { - queriesState[query] = req.query[query]; - } - }); - } - next(); -}; - const getDataZone = () => { let rtn; if (opennebulaZones && Array.isArray(opennebulaZones)) { - const { federation } = paramsState; + const { federation } = getParamsState(); rtn = opennebulaZones[0]; if (federation !== null) { rtn = opennebulaZones.find( @@ -180,18 +86,18 @@ router.all( if (zone) { const { RPC } = zone; const connectOpennebula = ( - user = userOpennebula, - pass = passOpennebula + user = getUserOpennebula(), + pass = getPassOpennebula() ) => opennebulaConnect(user, pass, RPC); const { resource } = req.params; const routeFunction = checkRouteFunction(resource); res.locals.httpCode = Map(methodNotAllowed).toObject(); const dataSources = { - [fromData.resource]: paramsState, - [fromData.query]: queriesState, + [fromData.resource]: getParamsState(), + [fromData.query]: getQueriesState(), [fromData.postBody]: req.body }; - if (routeFunction !== undefined) { + if (routeFunction) { const valRouteFunction = validateRouteFunction( routeFunction, httpMethod @@ -202,13 +108,13 @@ router.all( res, next, connectOpennebula, - idUserOpennebula + getIdUserOpennebula() ); } else { next(); } } else { - const { method } = paramsState; + const { method } = getParamsState(); const command = commandXML( resource, method, @@ -232,8 +138,8 @@ router.all( } }; const connect = connectOpennebula( - userOpennebula, - passOpennebula, + getUserOpennebula(), + getPassOpennebula(), RPC ); connect( diff --git a/src/fireedge/src/routes/public.js b/src/fireedge/src/routes/entrypoints/App.js similarity index 92% rename from src/fireedge/src/routes/public.js rename to src/fireedge/src/routes/entrypoints/App.js index 2d344b772e5..e25f04375ae 100644 --- a/src/fireedge/src/routes/public.js +++ b/src/fireedge/src/routes/entrypoints/App.js @@ -21,14 +21,14 @@ const { createStore, compose, applyMiddleware } = require('redux'); const thunk = require('redux-thunk').default; const classnames = require('classnames'); const { ServerStyleSheets } = require('@material-ui/core/styles'); -const rootReducer = require('../public/reducers'); -const App = require('../public/app').default; -const { getConfig } = require('../utils/yml-connect'); +const rootReducer = require('../../public/reducers'); +const App = require('../../public/app').default; +const { getConfig } = require('../../utils/yml'); const { includeMAPSJSbyHTML, includeJSbyHTML, includeCSSbyHTML -} = require('../utils'); +} = require('../../utils'); const router = express.Router(); @@ -37,6 +37,7 @@ router.get('*', (req, res) => { const pathPublic = `${__dirname}/public`; const composeEnhancer = + // eslint-disable-next-line no-underscore-dangle (root && root.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; const store = createStore( diff --git a/src/fireedge/src/routes/entrypoints/index.js b/src/fireedge/src/routes/entrypoints/index.js new file mode 100644 index 00000000000..bf1e9dc2961 --- /dev/null +++ b/src/fireedge/src/routes/entrypoints/index.js @@ -0,0 +1,24 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const entrypoint404 = require('server-entrypoints/404'); +const entrypointApi = require('server-entrypoints/Api'); +const entrypointApp = require('server-entrypoints/App'); + +module.exports = { + entrypoint404, + entrypointApi, + entrypointApp +}; diff --git a/src/fireedge/src/routes/entrypoints/middlewares/api/index.js~HEAD b/src/fireedge/src/routes/entrypoints/middlewares/api/index.js~HEAD new file mode 100644 index 00000000000..b863ceaf671 --- /dev/null +++ b/src/fireedge/src/routes/entrypoints/middlewares/api/index.js~HEAD @@ -0,0 +1,142 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ +const { Map } = require('immutable'); +const { + private: authenticated, + public: nonAuthenticated +} = require('../../../api'); +const { httpCodes, params } = require('../../../../utils/contants'); +const { + validateAuth, + getAllowedQueryParams, + createParamsState, + createQueriesState +} = require('../../../../utils'); + +const defaultParams = Map(createParamsState()); +const defaultQueries = Map(createQueriesState()); + +let paramsState = defaultParams.toObject(); +let queriesState = defaultQueries.toObject(); +let idUserOpennebula = ''; +let userOpennebula = ''; +let passOpennebula = ''; + +const getParamsState = () => paramsState; +const getQueriesState = () => queriesState; +const getIdUserOpennebula = () => idUserOpennebula; +const getUserOpennebula = () => userOpennebula; +const getPassOpennebula = () => passOpennebula; + +const validateResource = (req, res, next) => { + const { badRequest, unauthorized, serviceUnavailable } = httpCodes; + let status = badRequest; + if (req && req.params && req.params.resource) { + const resource = req.params.resource; + status = serviceUnavailable; + if (resource in authenticated) { + const session = validateAuth(req); + if ( + session && + 'iss' in session && + 'aud' in session && + 'jti' in session && + 'iat' in session && + 'exp' in session + ) { + idUserOpennebula = session.iss; + userOpennebula = session.aud; + passOpennebula = session.jti; + next(); + return; + } + status = unauthorized; + } + if (resource in nonAuthenticated) { + next(); + return; + } + } + res.status(status.id).json(status); +}; + +const optionalParameters = (req, res, next) => { + if (req && req.params) { + let start = true; + const keys = Object.keys(req.params); + keys.forEach(param => { + if (start) { + start = false; + return start; + } + if (req.params[param]) { + const matches = req.params[param].match(/(^[\w]*=)/gi); + if (matches && matches[0]) { + params.forEach(parameter => { + if ( + matches[0].replace(/=/g, '').toLowerCase() === + parameter.toLowerCase() + ) { + const removeKey = new RegExp(`^${parameter}=`, 'i'); + if (paramsState[parameter] === null) { + paramsState[parameter] = req.params[param].replace( + removeKey, + '' + ); + } + } + }); + } else { + paramsState[param] = req.params[param]; + } + } + return ''; + }); + } + next(); +}; + +const optionalQueries = (req, res, next) => { + if (req && req.query) { + const keys = Object.keys(req.query); + const queries = getAllowedQueryParams(); + keys.forEach(query => { + if (req.query[query] && queries.includes(query)) { + queriesState[query] = req.query[query]; + } + }); + } + next(); +}; + +const clearStates = () => { + paramsState = defaultParams.toObject(); + queriesState = defaultQueries.toObject(); + idUserOpennebula = ''; + userOpennebula = ''; + passOpennebula = ''; +}; + +module.exports = { + validateResource, + optionalParameters, + optionalQueries, + clearStates, + getParamsState, + getQueriesState, + getIdUserOpennebula, + getUserOpennebula, + getPassOpennebula +}; diff --git a/src/fireedge/src/config/commands/acl-commands.js b/src/fireedge/src/utils/contants/commands/acl.js similarity index 100% rename from src/fireedge/src/config/commands/acl-commands.js rename to src/fireedge/src/utils/contants/commands/acl.js diff --git a/src/fireedge/src/config/commands/cluster-commands.js b/src/fireedge/src/utils/contants/commands/cluster.js similarity index 100% rename from src/fireedge/src/config/commands/cluster-commands.js rename to src/fireedge/src/utils/contants/commands/cluster.js diff --git a/src/fireedge/src/config/commands/datastore-commands.js b/src/fireedge/src/utils/contants/commands/datastore.js similarity index 100% rename from src/fireedge/src/config/commands/datastore-commands.js rename to src/fireedge/src/utils/contants/commands/datastore.js diff --git a/src/fireedge/src/config/commands/document-commands.js b/src/fireedge/src/utils/contants/commands/document.js similarity index 100% rename from src/fireedge/src/config/commands/document-commands.js rename to src/fireedge/src/utils/contants/commands/document.js diff --git a/src/fireedge/src/config/commands/group-commands.js b/src/fireedge/src/utils/contants/commands/group.js similarity index 100% rename from src/fireedge/src/config/commands/group-commands.js rename to src/fireedge/src/utils/contants/commands/group.js diff --git a/src/fireedge/src/config/commands/groupsec-commands.js b/src/fireedge/src/utils/contants/commands/groupsec.js similarity index 100% rename from src/fireedge/src/config/commands/groupsec-commands.js rename to src/fireedge/src/utils/contants/commands/groupsec.js diff --git a/src/fireedge/src/config/commands/hook-commands.js b/src/fireedge/src/utils/contants/commands/hook.js similarity index 100% rename from src/fireedge/src/config/commands/hook-commands.js rename to src/fireedge/src/utils/contants/commands/hook.js diff --git a/src/fireedge/src/config/commands/host-commands.js b/src/fireedge/src/utils/contants/commands/host.js similarity index 100% rename from src/fireedge/src/config/commands/host-commands.js rename to src/fireedge/src/utils/contants/commands/host.js diff --git a/src/fireedge/src/config/commands/image-commands.js b/src/fireedge/src/utils/contants/commands/image.js similarity index 100% rename from src/fireedge/src/config/commands/image-commands.js rename to src/fireedge/src/utils/contants/commands/image.js diff --git a/src/fireedge/src/config/commands-params.js b/src/fireedge/src/utils/contants/commands/index.js similarity index 61% rename from src/fireedge/src/config/commands-params.js rename to src/fireedge/src/utils/contants/commands/index.js index 587d915ba52..14a473194a1 100644 --- a/src/fireedge/src/config/commands-params.js +++ b/src/fireedge/src/utils/contants/commands/index.js @@ -13,27 +13,27 @@ /* limitations under the License. */ /* -------------------------------------------------------------------------- */ -const { from, httpMethod } = require('./defaults'); -const acl = require('./commands/acl-commands'); -const cluster = require('./commands/cluster-commands'); -const datastore = require('./commands/datastore-commands'); -const document = require('./commands/document-commands'); -const group = require('./commands/group-commands'); -const groupsec = require('./commands/groupsec-commands'); -const hook = require('./commands/hook-commands'); -const host = require('./commands/host-commands'); -const image = require('./commands/image-commands'); -const market = require('./commands/market-commands'); -const system = require('./commands/system-commands'); -const template = require('./commands/template-commands'); -const user = require('./commands/user-commands'); -const vdc = require('./commands/vdc-commans'); -const vm = require('./commands/vm-commands'); -const vmgroup = require('./commands/vmgroup-commands'); -const vn = require('./commands/vn-commands'); -const vntemplate = require('./commands/vntemplate-commands'); -const vrouter = require('./commands/vrouter-commands'); -const zone = require('./commands/zone-commands'); +const { from, httpMethod } = require('../defaults'); +const acl = require('./acl'); +const cluster = require('./cluster'); +const datastore = require('./datastore'); +const document = require('./document'); +const group = require('./group'); +const groupsec = require('./groupsec'); +const hook = require('./hook'); +const host = require('./host'); +const image = require('./image'); +const market = require('./market'); +const system = require('./system'); +const template = require('./template'); +const user = require('./user'); +const vdc = require('./vdc'); +const vm = require('./vm'); +const vmgroup = require('./vmgroup'); +const vn = require('./vn'); +const vntemplate = require('./vntemplate'); +const vrouter = require('./vrouter'); +const zone = require('./zone'); module.exports = { ...acl(from, httpMethod), diff --git a/src/fireedge/src/config/commands/market-commands.js b/src/fireedge/src/utils/contants/commands/market.js similarity index 100% rename from src/fireedge/src/config/commands/market-commands.js rename to src/fireedge/src/utils/contants/commands/market.js diff --git a/src/fireedge/src/config/commands/system-commands.js b/src/fireedge/src/utils/contants/commands/system.js similarity index 100% rename from src/fireedge/src/config/commands/system-commands.js rename to src/fireedge/src/utils/contants/commands/system.js diff --git a/src/fireedge/src/config/commands/template-commands.js b/src/fireedge/src/utils/contants/commands/template.js similarity index 100% rename from src/fireedge/src/config/commands/template-commands.js rename to src/fireedge/src/utils/contants/commands/template.js diff --git a/src/fireedge/src/config/commands/user-commands.js b/src/fireedge/src/utils/contants/commands/user.js similarity index 100% rename from src/fireedge/src/config/commands/user-commands.js rename to src/fireedge/src/utils/contants/commands/user.js diff --git a/src/fireedge/src/config/commands/vdc-commans.js b/src/fireedge/src/utils/contants/commands/vdc.js similarity index 100% rename from src/fireedge/src/config/commands/vdc-commans.js rename to src/fireedge/src/utils/contants/commands/vdc.js diff --git a/src/fireedge/src/config/commands/vm-commands.js b/src/fireedge/src/utils/contants/commands/vm.js similarity index 100% rename from src/fireedge/src/config/commands/vm-commands.js rename to src/fireedge/src/utils/contants/commands/vm.js diff --git a/src/fireedge/src/config/commands/vmgroup-commands.js b/src/fireedge/src/utils/contants/commands/vmgroup.js similarity index 100% rename from src/fireedge/src/config/commands/vmgroup-commands.js rename to src/fireedge/src/utils/contants/commands/vmgroup.js diff --git a/src/fireedge/src/config/commands/vn-commands.js b/src/fireedge/src/utils/contants/commands/vn.js similarity index 100% rename from src/fireedge/src/config/commands/vn-commands.js rename to src/fireedge/src/utils/contants/commands/vn.js diff --git a/src/fireedge/src/config/commands/vntemplate-commands.js b/src/fireedge/src/utils/contants/commands/vntemplate.js similarity index 100% rename from src/fireedge/src/config/commands/vntemplate-commands.js rename to src/fireedge/src/utils/contants/commands/vntemplate.js diff --git a/src/fireedge/src/config/commands/vrouter-commands.js b/src/fireedge/src/utils/contants/commands/vrouter.js similarity index 100% rename from src/fireedge/src/config/commands/vrouter-commands.js rename to src/fireedge/src/utils/contants/commands/vrouter.js diff --git a/src/fireedge/src/config/commands/zone-commands.js b/src/fireedge/src/utils/contants/commands/zone.js similarity index 100% rename from src/fireedge/src/config/commands/zone-commands.js rename to src/fireedge/src/utils/contants/commands/zone.js diff --git a/src/fireedge/src/config/defaults.js b/src/fireedge/src/utils/contants/defaults.js similarity index 100% rename from src/fireedge/src/config/defaults.js rename to src/fireedge/src/utils/contants/defaults.js diff --git a/src/fireedge/src/config/http-codes.js b/src/fireedge/src/utils/contants/http-codes.js similarity index 100% rename from src/fireedge/src/config/http-codes.js rename to src/fireedge/src/utils/contants/http-codes.js diff --git a/src/fireedge/src/utils/contants/index.js b/src/fireedge/src/utils/contants/index.js new file mode 100644 index 00000000000..028434a5541 --- /dev/null +++ b/src/fireedge/src/utils/contants/index.js @@ -0,0 +1,26 @@ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +const defaults = require('./defaults'); +const httpCodes = require('./http-codes'); +const params = require('./params'); +const opennebulaCommands = require('./commands'); + +module.exports = { + defaults, + httpCodes, + params, + opennebulaCommands +}; \ No newline at end of file diff --git a/src/fireedge/src/config/params.js b/src/fireedge/src/utils/contants/params.js similarity index 100% rename from src/fireedge/src/config/params.js rename to src/fireedge/src/utils/contants/params.js diff --git a/src/fireedge/src/utils/general-functions.js b/src/fireedge/src/utils/general.js similarity index 100% rename from src/fireedge/src/utils/general-functions.js rename to src/fireedge/src/utils/general.js diff --git a/src/fireedge/src/utils/index.js b/src/fireedge/src/utils/index.js index dfc5639125e..c7fe1c3c01d 100644 --- a/src/fireedge/src/utils/index.js +++ b/src/fireedge/src/utils/index.js @@ -14,13 +14,13 @@ /* -------------------------------------------------------------------------- */ const fs = require('fs-extra'); -const params = require('../config/params'); -const { defaultTypeLog } = require('../config/defaults'); -const functionRoutes = require('../config/function-routes'); -const { validateAuth } = require('./jwt-functions'); -const { messageTerminal } = require('./general-functions'); +const params = require('./contants/params'); +const { defaultTypeLog } = require('./contants/defaults'); +const functionRoutes = require('../routes/api'); +const { validateAuth } = require('./jwt'); +const { messageTerminal } = require('./general'); const { addWsServer } = require('./ws-zeromq'); -const { getConfig } = require('./yml-connect'); +const { getConfig } = require('./yml'); // user config const appConfig = getConfig(); @@ -35,12 +35,12 @@ const { getAllowedQueryParams, getRouteForOpennebulaCommand, checkOpennebulaCommand -} = require('./opennebula-functions'); +} = require('./opennebula'); const createParamsState = () => { const rtn = {}; if (params && Array.isArray(params)) { - params.map(param => { + params.forEach(param => { rtn[param] = null; }); } @@ -51,7 +51,7 @@ const createQueriesState = () => { const rtn = {}; const queries = getAllowedQueryParams(); if (queries && Array.isArray(queries)) { - queries.map(query => { + queries.forEach(query => { rtn[query] = null; }); } @@ -61,7 +61,7 @@ const createQueriesState = () => { const includeMAPSJSbyHTML = (path = '') => { let scripts = ''; if (mode === defaultTypeLog) { - fs.readdirSync(path).map(file => { + fs.readdirSync(path).forEach(file => { if (file.match(/\w*\.js\.map+$\b/gi)) { scripts += ``; } @@ -72,7 +72,7 @@ const includeMAPSJSbyHTML = (path = '') => { const includeJSbyHTML = (path = '') => { let scripts = ''; - fs.readdirSync(path).map(file => { + fs.readdirSync(path).forEach(file => { if (file.match(/\w*\.js+$\b/gi)) { scripts += ``; } @@ -82,7 +82,7 @@ const includeJSbyHTML = (path = '') => { const includeCSSbyHTML = (path = '') => { let scripts = ''; - fs.readdirSync(path).map(file => { + fs.readdirSync(path).forEach(file => { if (file.match(/\w*\.css+$\b/gi)) { scripts += ``; } @@ -100,13 +100,12 @@ const validateRouteFunction = (routeFunction, httpMethod = '') => { typeof routeFunction.action === 'function' ) { rtn = routeFunction.action; - console.log("aca llama la ruta--->", rtn.toString()) } return rtn; }; const checkRouteFunction = route => { - let rtn; + let rtn = false; const { private: functionPrivate, public: functionPublic } = functionRoutes; const functions = { ...functionPrivate, ...functionPublic }; if (route in functions) { @@ -132,5 +131,6 @@ module.exports = { checkRouteFunction, checkOpennebulaCommand, validateRouteFunction, - responseOpennebula + responseOpennebula, + getConfig }; diff --git a/src/fireedge/src/utils/jwt-functions.js b/src/fireedge/src/utils/jwt.js similarity index 95% rename from src/fireedge/src/utils/jwt-functions.js rename to src/fireedge/src/utils/jwt.js index 00cd103140b..5001122b79a 100644 --- a/src/fireedge/src/utils/jwt-functions.js +++ b/src/fireedge/src/utils/jwt.js @@ -15,8 +15,8 @@ const jwt = require('jwt-simple'); const moment = require('moment'); -const { messageTerminal } = require('../utils/general-functions'); -const { getConfig } = require('./yml-connect'); +const { messageTerminal } = require('./general'); +const { getConfig } = require('./yml'); // user config const appConfig = getConfig(); diff --git a/src/fireedge/src/utils/opennebula-functions.js b/src/fireedge/src/utils/opennebula.js similarity index 93% rename from src/fireedge/src/utils/opennebula-functions.js rename to src/fireedge/src/utils/opennebula.js index 8c54fee25a1..dc439b3fec5 100644 --- a/src/fireedge/src/utils/opennebula-functions.js +++ b/src/fireedge/src/utils/opennebula.js @@ -19,16 +19,16 @@ const rpc = require('xmlrpc'); const xml2js = require('xml2js'); const { Map } = require('immutable'); const { sprintf } = require('sprintf-js'); -const httpCodes = require('../config/http-codes'); -const commandsParams = require('../config/commands-params'); +const httpCodes = require('./contants/http-codes'); +const commandsParams = require('./contants/commands'); const { from, defaultNamespace, defaultMessageProblemOpennebula -} = require('../config/defaults'); +} = require('./contants/defaults'); // user config -const { getConfig } = require('./yml-connect'); +const { getConfig } = require('./yml'); const appConfig = getConfig(); const namespace = appConfig.namespace || defaultNamespace; @@ -104,7 +104,7 @@ const getMethodForOpennebulaCommand = () => { const rtn = []; if (commandsParams) { const commands = Object.keys(commandsParams); - commands.map(command => { + commands.forEach(command => { if (command && command.length) { const commandString = command.split('.'); if (!rtn.includes(commandString[1])) { @@ -135,11 +135,11 @@ const getAllowedQueryParams = () => { const allowedQuerys = Object.keys(commandsParams); if (from && from.query) { const { query } = from; - allowedQuerys.map(allowedQuery => { + allowedQuerys.forEach(allowedQuery => { const command = commandsParams[allowedQuery]; if (command && command.params) { const internalParams = Object.keys(command.params); - internalParams.map(internalParam => { + internalParams.forEach(internalParam => { if ( command.params[internalParam] && command.params[internalParam].from && @@ -156,14 +156,14 @@ const getAllowedQueryParams = () => { }; const getRouteForOpennebulaCommand = () => { - const rtn = []; + const rtn = {}; if (commandsParams) { const commands = Object.keys(commandsParams); - commands.map(command => { + commands.forEach(command => { if (command && command.length) { const commandString = command.split('.'); - if (!rtn.includes(commandString[0])) { - rtn.push(commandString[0]); + if (!(commandString[0] in rtn)) { + rtn[commandString[0]] = false; // false is a opennebula command } } }); @@ -175,7 +175,7 @@ const checkPositionInDataSource = dataSource => { let rtn = true; if (dataSource && from) { const fromKeys = Object.values(from); - fromKeys.map(key => { + fromKeys.forEach(key => { if (!(key && dataSource && key in dataSource)) { rtn = false; } @@ -204,7 +204,7 @@ const checkOpennebulaCommand = ( if (dataSource && checkPositionInDataSource(dataSource)) { const { params: paramsForCommand } = commandsParams[command]; const internalParams = []; - Object.keys(paramsForCommand).map(param => { + Object.keys(paramsForCommand).forEach(param => { const parameter = paramsForCommand[param]; if ( 'default' in parameter && @@ -241,7 +241,7 @@ const paramsDefaultByCommandOpennebula = (command = '', httpCode = '') => { const getDefaultValues = checkOpennebulaCommand(command, httpCode, false); if (getDefaultValues && getDefaultValues.params) { const defaultParams = Object.keys(getDefaultValues.params); - defaultParams.map(defaultParam => { + defaultParams.forEach(defaultParam => { if ( getDefaultValues.params && defaultParam && diff --git a/src/fireedge/src/utils/ws-zeromq.js b/src/fireedge/src/utils/ws-zeromq.js index 6f1dd0a3fcd..0e05a898e6a 100644 --- a/src/fireedge/src/utils/ws-zeromq.js +++ b/src/fireedge/src/utils/ws-zeromq.js @@ -19,10 +19,10 @@ const { server: Server } = require('websocket'); const { socket } = require('zeromq'); const xml2js = require('xml2js'); -const { messageTerminal } = require('./general-functions'); -const { getConfig } = require('./yml-connect'); -const { validateAuth } = require('./jwt-functions'); -const { unauthorized } = require('../config/http-codes'); +const { messageTerminal } = require('./general'); +const { getConfig } = require('./yml'); +const { validateAuth } = require('./jwt'); +const { unauthorized } = require('./contants/http-codes'); // user config const appConfig = getConfig(); @@ -67,7 +67,7 @@ const addWsServer = appServer => { zeromqSock.on('message', (...args) => { const mssgs = []; // broadcast - clients.map(client => { + clients.forEach(client => { Array.prototype.slice.call(args).forEach(arg => { mssgs.push(arg.toString()); }); diff --git a/src/fireedge/src/utils/yml-connect.js b/src/fireedge/src/utils/yml.js similarity index 96% rename from src/fireedge/src/utils/yml-connect.js rename to src/fireedge/src/utils/yml.js index e51a08708f2..7a5fb071194 100644 --- a/src/fireedge/src/utils/yml-connect.js +++ b/src/fireedge/src/utils/yml.js @@ -15,7 +15,7 @@ const fs = require('fs-extra'); const YAML = require('yaml'); -const { defaultConfigFile } = require('../config/defaults'); +const { defaultConfigFile } = require('./contants/defaults'); const getConfig = () => { let rtn = {}; diff --git a/src/fireedge/webpack.config.js b/src/fireedge/webpack.config.js index f5a6c32f663..4541fc742c0 100644 --- a/src/fireedge/webpack.config.js +++ b/src/fireedge/webpack.config.js @@ -15,14 +15,14 @@ const nodeExternals = require('webpack-node-externals'); const path = require('path'); -const { getConfig } = require('./src/utils/yml-connect'); -const { defaultWebpackMode } = require('./src/config/defaults'); - -const appConfig = getConfig(); +const { getConfig } = require('./src/utils/'); +const { defaultWebpackMode } = require('./src/utils/contants'); // settings +const appConfig = getConfig(); const mode = appConfig.MODE || defaultWebpackMode; const devtool = mode === defaultWebpackMode ? 'inline-source-map' : ''; + const js = { test: /\.js$/, exclude: /node_modules/, @@ -39,6 +39,15 @@ const js = { } } }; +const alias = { + alias: { + server: path.resolve(__dirname, 'src/'), + client: path.resolve(__dirname, 'src/public/'), + 'server-entrypoints': path.resolve(__dirname, 'src/routes/entrypoints/'), + 'server-api': path.resolve(__dirname, 'src/routes/api/') + }, + extensions: [".js"] +}; const serverConfig = { mode, @@ -57,6 +66,7 @@ const serverConfig = { path: path.resolve(__dirname, 'dist'), filename: '[name]' }, + resolve: alias, devtool }; @@ -78,6 +88,7 @@ const clientConfig = { path: path.resolve(__dirname, 'dist/public'), filename: '[name]' }, + resolve: alias, devtool };