diff --git a/README.md b/README.md index 174efafa..0f7a0002 100644 --- a/README.md +++ b/README.md @@ -221,3 +221,12 @@ To generate a new schema, use the management command ``` python manage.py export_schema_for_cypress ``` + + +## GraphQL + +### Generate GraphQL SDL Document + +``` +python manage.py export_schema_graphql +``` diff --git a/client/cypress.json b/client/cypress.json index 778d1157..288f4ea2 100644 --- a/client/cypress.json +++ b/client/cypress.json @@ -5,5 +5,6 @@ "reporterOptions": { "mochaFile": "cypress/test-reports/cypress-results-[hash].xml", "toConsole": true - } + }, + "$schema": "https://on.cypress.io/cypress.schema.json" } diff --git a/client/cypress/.eslintrc.js b/client/cypress/.eslintrc.js new file mode 100644 index 00000000..33c8fd31 --- /dev/null +++ b/client/cypress/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + "extends": [ + "plugin:cypress/recommended" + ] +} diff --git a/client/cypress/integration/room-page.spec.js b/client/cypress/integration/rooms/room-page.spec.js similarity index 100% rename from client/cypress/integration/room-page.spec.js rename to client/cypress/integration/rooms/room-page.spec.js diff --git a/client/cypress/integration/rooms-page.spec.js b/client/cypress/integration/rooms/rooms-page.spec.js similarity index 100% rename from client/cypress/integration/rooms-page.spec.js rename to client/cypress/integration/rooms/rooms-page.spec.js diff --git a/client/cypress/integration/sync-module-visibility.spec.js b/client/cypress/integration/sync-module-visibility.spec.js new file mode 100644 index 00000000..af91d95c --- /dev/null +++ b/client/cypress/integration/sync-module-visibility.spec.js @@ -0,0 +1,74 @@ +// import * as schema from '../fixtures/schema.json'; +import {getModules, getMe} from '../support/helpers'; + +const mocks = { + UUID: () => 'Whatever', + GenericStreamFieldType: () => [], +}; + +const operations = { + MeQuery() { + return getMe({ + schoolClasses: ['FLID2018a', 'Andere Klasse'], + teacher: true, + }); + }, + ModulesQuery: getModules, + UpdateSettings: { + updateSettings: { + success: true, + }, + }, + MySchoolClassQuery: { + me: {}, + }, + UpdateLastModule: { + updateLastModule: { + success: true, + }, + }, + SyncModuleVisibility: { + syncModuleVisibility: { + success: true, + }, + }, +}; + +describe('Apply module visibility', () => { + beforeEach(() => { + cy.server(); + cy.task('getSchema').then(schema => { + cy.mockGraphql({ + schema, + // endpoint: '/api/graphql' + mocks, + operations, + }); + }); + }); + + it('needs to be implemented', () => { + // Cypress.config({ + // baseUrl: 'http://localhost:8080', + // }); + cy.viewport('macbook-15'); + + // login as teacher + // cy.fakeLogin('nico.zickgraf', 'test'); + cy.apolloLogin('nico.zickgraf', 'test'); + // cy.wait('@gqlBetaLogin'); + // go to module + cy.visit('/module/lohn-und-budget'); + cy.selectClass('Andere Klasse'); + // click on settings + cy.getByDataCy('module-settings-button').click(); + // click on select button + cy.getByDataCy('select-school-class-button').click(); + // select schoolclass + cy.getByDataCy('school-class-visibility-dropdown').select('FLID2018a'); + // save changes + cy.getByDataCy('save-visibility-button').click(); + + cy.getByDataCy('module-title').should('exist'); + }); +}); diff --git a/client/cypress/integration/beta-login.spec.js b/client/cypress/integration/users/beta-login.spec.js similarity index 100% rename from client/cypress/integration/beta-login.spec.js rename to client/cypress/integration/users/beta-login.spec.js diff --git a/client/cypress/integration/coupon.spec.js b/client/cypress/integration/users/coupon.spec.js similarity index 94% rename from client/cypress/integration/coupon.spec.js rename to client/cypress/integration/users/coupon.spec.js index 595f503b..6fcd9931 100644 --- a/client/cypress/integration/coupon.spec.js +++ b/client/cypress/integration/users/coupon.spec.js @@ -1,6 +1,6 @@ import { GraphQLError } from 'graphql'; -const schema = require('../fixtures/schema.json'); +const schema = require('../../fixtures/schema.json'); describe('Email Verifcation', () => { beforeEach(() => { @@ -19,7 +19,7 @@ describe('Email Verifcation', () => { }, } }); - cy.apolloLogin('rahel.cueni', 'test') + cy.apolloLogin('rahel.cueni', 'test'); cy.visit('/license-activation'); cy.redeemCoupon('12345asfd'); diff --git a/client/cypress/integration/login.spec.js b/client/cypress/integration/users/login.spec.js similarity index 96% rename from client/cypress/integration/login.spec.js rename to client/cypress/integration/users/login.spec.js index 3cb5d56b..54d96134 100644 --- a/client/cypress/integration/login.spec.js +++ b/client/cypress/integration/users/login.spec.js @@ -1,4 +1,4 @@ -const schema = require('../fixtures/schema_public.json'); +const schema = require('../../fixtures/schema_public.json'); const isEmailAvailableUrl = '**/rest/deutsch/V1/customers/isEmailAvailable'; const checkPasswordUrl = '**/rest/deutsch/V1/integration/customer/token'; @@ -20,7 +20,7 @@ describe('Login', () => { message: 'success', success: true } - } + }; }, } }); @@ -69,5 +69,5 @@ describe('Login', () => { cy.checkEmailAvailable('feuzaebi.ch'); cy.get('[data-cy="email-local-errors"]').contains('Bitte geben Sie eine gülitge E-Mail an'); - }) + }); }); diff --git a/client/cypress/plugins/index.js b/client/cypress/plugins/index.js index fd170fba..4117cf88 100644 --- a/client/cypress/plugins/index.js +++ b/client/cypress/plugins/index.js @@ -11,7 +11,18 @@ // This function is called when a project is opened or re-opened (e.g. due to // the project's config changing) +import {readFileSync} from 'fs'; +import {resolve} from 'path'; + module.exports = (on, config) => { // `on` is used to hook into various events Cypress emits // `config` is the resolved Cypress config -} + on('task', { + getSchema() { + return readFileSync( + resolve(__dirname, '../../../schema.graphql'), + 'utf8' + ); + } + }); +}; diff --git a/client/cypress/support/commands.js b/client/cypress/support/commands.js index abc4a852..1f153f78 100644 --- a/client/cypress/support/commands.js +++ b/client/cypress/support/commands.js @@ -161,7 +161,7 @@ Cypress.Commands.add('redeemCoupon', coupon => { cy.get('[data-cy="coupon-input"]').type(coupon); } cy.get('[data-cy="coupon-button"]').click(); -}) +}); Cypress.Commands.add('assertStartPage', (onboarding) => { if (onboarding) { @@ -174,3 +174,18 @@ Cypress.Commands.add('assertStartPage', (onboarding) => { Cypress.Commands.add('skipOnboarding', (onboarding) => { cy.get('[data-cy=onboarding-skip-link]').click(); }); + +Cypress.Commands.add('getByDataCy', (selector) => { + return cy.get(`[data-cy=${selector}]`); +}); + +Cypress.Commands.add('selectClass', (schoolClass) => { + cy.getByDataCy('user-widget-avatar').click(); + cy.getByDataCy('class-selection').click(); + cy.getByDataCy('class-selection-entry').contains(schoolClass).click(); +}); + +Cypress.Commands.add('fakeLogin', () => { + cy.log('Logging in (fake)'); + cy.setCookie('loginStatus', 'true'); +}); diff --git a/client/cypress/support/helpers.js b/client/cypress/support/helpers.js new file mode 100644 index 00000000..2cae9751 --- /dev/null +++ b/client/cypress/support/helpers.js @@ -0,0 +1,353 @@ +const getSchoolClassNode = (id, schoolClassName) => ({ + 'id': btoa(`SchoolClassNode:${id}`), + 'name': schoolClassName, + '__typename': 'SchoolClassNode', +}); + +export const getMe = ({schoolClasses, teacher}) => { + let schoolClassNodes; + if (schoolClasses) { + schoolClassNodes = schoolClasses.map((schoolClass, i) => getSchoolClassNode(i, schoolClass)); + } else { + schoolClassNodes = [getSchoolClassNode(1, 'FLID2018a')]; + } + + return { + 'me': { + 'id': 'VXNlck5vZGU6NQ==', + 'pk': 5, + 'username': 'rahel.cueni', + 'email': 'rahel.cueni@skillbox.example', + 'expiryDate': '3596153600', + 'firstName': 'Rahel', + 'lastName': 'Cueni', + 'avatarUrl': '', + 'isTeacher': false, + 'lastModule': { + 'id': 'TW9kdWxlTm9kZToxNw==', + 'slug': 'lohn-und-budget', + '__typename': 'ModuleNode', + }, + 'selectedClass': { + 'id': 'U2Nob29sQ2xhc3NOb2RlOjI=', + '__typename': 'SchoolClassNode', + }, + 'lastTopic': { + 'id': 'VG9waWNOb2RlOjU=', + 'slug': 'geld-und-kauf', + '__typename': 'TopicNode', + }, + 'schoolClasses': { + 'edges': schoolClassNodes.map(scn => ({ + node: scn, + '__typename': 'SchoolClassNodeEdge', + })), + '__typename': 'SchoolClassNodeConnection', + }, + '__typename': 'UserNode', + 'onboardingVisited': true, + 'permissions': teacher ? ['users.can_manage_school_class_content'] : [], + }, + }; +}; + +export const getAssignments = () => { + return { + 'assignments': { + 'edges': [ + { + 'node': { + 'id': 'QXNzaWdubWVudE5vZGU6MQ==', + 'title': 'Ein Auftragstitel', + 'assignment': 'Ein Auftrag', + 'solution': null, + 'submission': { + 'id': 'U3R1ZGVudFN1Ym1pc3Npb25Ob2RlOjE=', + 'text': 'Hir ist ein Feler gewesen', + 'final': false, + 'document': '', + 'submissionFeedback': { + 'id': 'U3VibWlzc2lvbkZlZWRiYWNrTm9kZTox', + 'text': '\ud83d\ude42\ud83d\ude10\ud83e\udd2c\ud83d\udc4d\ud83e\udd22\ud83e\udd22\ud83e\udd22\ud83e\udd22\ud83d\ude2e\ud83e\udd17', + 'teacher': { + 'firstName': 'Nico', + 'lastName': 'Zickgraf', + '__typename': 'UserNode', + }, + '__typename': 'SubmissionFeedbackNode', + }, + '__typename': 'StudentSubmissionNode', + }, + '__typename': 'AssignmentNode', + }, + '__typename': 'AssignmentNodeEdge', + }, + ], + '__typename': 'AssignmentNodeConnection', + }, + }; +}; + +export const getModules = () => { + return { + 'lohn-und-budget': { + 'id': 'TW9kdWxlTm9kZToyOA==', + 'title': 'Lohn und Budget', + 'metaTitle': 'Modul 1', + 'teaser': 'Die Berufsbildung ist ein neuer Lebensabschnit', + 'intro': '\n

Sie stehen am Anfang eines neuen Lebensabschnitts. In Ihrer Rolle als Berufslernende oder Berufslernender haben Sie Verantwortung übernommen.

\n

Wie erging es Ihnen am ersten Arbeits- und Schultag?

\n ', + 'slug': 'lohn-und-budget', + 'heroImage': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==', + 'solutionsEnabled': false, + 'bookmark': { + 'note': null, + '__typename': 'ModuleBookmarkNode', + }, + '__typename': 'ModuleNode', + 'assignments': { + 'edges': [ + { + 'node': { + 'id': 'QXNzaWdubWVudE5vZGU6MQ==', + 'title': 'Ein Auftragstitel', + 'assignment': 'Ein Auftrag', + 'solution': null, + 'submission': { + 'id': 'U3R1ZGVudFN1Ym1pc3Npb25Ob2RlOjE=', + 'text': 'Hir ist ein Feler gewesen', + 'final': false, + 'document': '', + 'submissionFeedback': { + 'id': 'U3VibWlzc2lvbkZlZWRiYWNrTm9kZTox', + 'text': '🙂😐🤬👍🤢🤢🤢🤢😮🤗', + 'teacher': { + 'firstName': 'Nico', + 'lastName': 'Zickgraf', + '__typename': 'UserNode', + }, + '__typename': 'SubmissionFeedbackNode', + }, + '__typename': 'StudentSubmissionNode', + }, + '__typename': 'AssignmentNode', + }, + '__typename': 'AssignmentNodeEdge', + }, + ], + '__typename': 'AssignmentNodeConnection', + }, + 'objectiveGroups': { + 'edges': [], + '__typename': 'ObjectiveGroupNodeConnection', + }, + 'chapters': { + 'edges': [ + { + 'node': { + 'id': 'Q2hhcHRlck5vZGU6MTg=', + 'title': '1.1 Lehrbeginn', + 'description': 'Wie sieht Ihr Konsumverhalten aus?', + 'bookmark': null, + 'contentBlocks': { + 'edges': [ + { + 'node': { + 'id': 'Q29udGVudEJsb2NrTm9kZToxOQ==', + 'slug': 'assignment', + 'title': 'Assignment', + 'type': 'NORMAL', + 'contents': [ + { + 'type': 'assignment', + 'value': { + 'title': 'Ein Auftragstitel', + 'assignment': 'Ein Auftrag', + 'id': 'QXNzaWdubWVudE5vZGU6MQ==', + }, + 'id': 'df8212ee-3e82-49fa-977e-c4b60789163e', + }, + ], + 'userCreated': false, + 'mine': false, + 'bookmarks': [], + 'hiddenFor': { + 'edges': [], + '__typename': 'SchoolClassNodeConnection', + }, + 'visibleFor': { + 'edges': [], + '__typename': 'SchoolClassNodeConnection', + }, + '__typename': 'ContentBlockNode', + }, + '__typename': 'ContentBlockNodeEdge', + }, + ], + '__typename': 'ContentBlockNodeConnection', + }, + '__typename': 'ChapterNode', + }, + '__typename': 'ChapterNodeEdge', + }, + ], + '__typename': 'ChapterNodeConnection', + }, + }, + 'geld': { + 'id': 'TW9kdWxlTm9kZTo0Mg==', + 'title': 'Geld', + 'metaTitle': 'Modul 2', + 'teaser': ' Geld braucht jeder von uns im t\u00e4glichen Leben.', + 'intro': '\n

Jeder B\u00fcrger nutzt es. Nahezu jeden Tag. Kaum ein Tag vergeht, an dem wir nicht mit M\u00fcnzen oder Geldscheinen bezahlen, bargeldlose \u00dcberweisungen t\u00e4tigen oder andere Zahlungsmethoden verwenden. Doch was genau befindet sich da eigentlich in unserem Geldbeutel? Was ist das, was auf unseren Konten liegt und die Bezeichnung Geld tr\u00e4gt?

\n ', + 'slug': 'geld', + 'heroImage': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==', + 'solutionsEnabled': false, + 'bookmark': null, + '__typename': 'ModuleNode', + 'assignments': { + 'edges': [], + '__typename': 'AssignmentNodeConnection', + }, + 'objectiveGroups': { + 'edges': [], + '__typename': 'ObjectiveGroupNodeConnection', + }, + 'chapters': { + 'edges': [ + { + 'node': { + 'id': 'Q2hhcHRlck5vZGU6MzI=', + 'title': '2.1 Eine Welt ohne Geld?', + 'description': '', + 'bookmark': null, + 'contentBlocks': { + 'edges': [ + { + 'node': { + 'id': 'Q29udGVudEJsb2NrTm9kZToxOQ==', + 'slug': 'assignment', + 'title': 'Assignment', + 'type': 'NORMAL', + 'contents': [ + { + 'type': 'assignment', + 'value': { + 'title': 'Ein Auftragstitel', + 'assignment': 'Ein Auftrag', + 'id': 'QXNzaWdubWVudE5vZGU6MQ==', + }, + 'id': 'df8212ee-3e82-49fa-977e-c4b60789163e', + }, + ], + 'userCreated': false, + 'mine': false, + 'bookmarks': [], + 'hiddenFor': { + 'edges': [], + '__typename': 'SchoolClassNodeConnection', + }, + 'visibleFor': { + 'edges': [], + '__typename': 'SchoolClassNodeConnection', + }, + '__typename': 'ContentBlockNode', + }, + '__typename': 'ContentBlockNodeEdge', + }, + ], + '__typename': 'ContentBlockNodeConnection', + }, + '__typename': 'ChapterNode', + }, + '__typename': 'ChapterNodeEdge', + }, + ], + '__typename': 'ChapterNodeConnection', + }, + }, + 'lerntipps': { + 'id': 'TW9kdWxlTm9kZTo3MA==', + 'title': 'Lerntipps', + 'metaTitle': 'Modul 4', + 'teaser': 'Lerntipps', + 'intro': '\n

Sie stehen am Anfang eines neuen Lebensabschnitts. In Ihrer Rolle als Berufslernende oder Berufslernender haben Sie Verantwortung übernommen.

\n

Wie erging es Ihnen am ersten Arbeits- und Schultag?

\n ', + 'slug': 'lerntipps', + 'heroImage': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==', + 'solutionsEnabled': false, + 'bookmark': { + 'note': null, + '__typename': 'ModuleBookmarkNode', + }, + '__typename': 'ModuleNode', + 'assignments': { + 'edges': [], + '__typename': 'AssignmentNodeConnection', + }, + 'objectiveGroups': { + 'edges': [], + '__typename': 'ObjectiveGroupNodeConnection', + }, + 'chapters': { + 'edges': [ + { + 'node': { + 'id': 'Q2hhcHRlck5vZGU6MTg=', + 'title': '1.1 Lehrbeginn', + 'description': 'Wie sieht Ihr Konsumverhalten aus?', + 'bookmark': null, + 'contentBlocks': { + 'edges': [], + '__typename': 'ContentBlockNodeConnection', + }, + '__typename': 'ChapterNode', + }, + '__typename': 'ChapterNodeEdge', + }, + ], + '__typename': 'ChapterNodeConnection', + }, + }, + 'random': { + 'id': 'TW9kdWxlTm9kZTo1NA==', + 'title': 'Random', + 'metaTitle': 'Modul 5', + 'teaser': 'Random', + 'intro': '\n

Sie stehen am Anfang eines neuen Lebensabschnitts. In Ihrer Rolle als Berufslernende oder Berufslernender haben Sie Verantwortung übernommen.

\n

Wie erging es Ihnen am ersten Arbeits- und Schultag?

\n ', + 'slug': 'random', + 'heroImage': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==', + 'solutionsEnabled': false, + 'bookmark': { + 'note': null, + '__typename': 'ModuleBookmarkNode', + }, + '__typename': 'ModuleNode', + 'assignments': { + 'edges': [], + '__typename': 'AssignmentNodeConnection', + }, + 'objectiveGroups': { + 'edges': [], + '__typename': 'ObjectiveGroupNodeConnection', + }, + 'chapters': { + 'edges': [ + { + 'node': { + 'id': 'Q2hhcHRlck5vZGU6MTg=', + 'title': '1.1 Lehrbeginn', + 'description': 'Wie sieht Ihr Konsumverhalten aus?', + 'bookmark': null, + 'contentBlocks': { + 'edges': [], + '__typename': 'ContentBlockNodeConnection', + }, + '__typename': 'ChapterNode', + }, + '__typename': 'ChapterNodeEdge', + }, + ], + '__typename': 'ChapterNodeConnection', + }, + }, + }; +}; diff --git a/client/cypress/support/index.d.ts b/client/cypress/support/index.d.ts new file mode 100644 index 00000000..22da3dab --- /dev/null +++ b/client/cypress/support/index.d.ts @@ -0,0 +1,26 @@ +// Intellisense for custom Commands: https://github.com/cypress-io/cypress-example-todomvc#cypress-intellisense +declare namespace Cypress { + interface Chainable { + /** + * Login via API call to the GraphQL endpoint, without calling the frontend. Faster than the other login. + * @param username + * @param password + * @example + * cy.apolloLogin('nico.zickgraf', 'test') + */ + apolloLogin(username: string, password: string): Chainable + + /** + * Selects an element based on the `data-cy=xxx` attribute + * @param selector - The value of the data-cy attribute to select + * @example + * cy.getByDataCy('my-new-button') + */ + getByDataCy(selector: string): Chainable + + selectClass(schoolClass: string): void + + login(username:string, password:string, visitLogin?: boolean): void + fakeLogin(username:string, password:string): void + } +} diff --git a/client/cypress/tsconfig.json b/client/cypress/tsconfig.json new file mode 100644 index 00000000..3dcd08d5 --- /dev/null +++ b/client/cypress/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "resolveJsonModule": true, + "types": [ + "cypress" + ] + }, + "include": [ + "**/*.*" + ] +} diff --git a/client/package-lock.json b/client/package-lock.json index d0a4d8f1..d6bac1c2 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4,6 +4,23 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@ardatan/aggregate-error": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz", + "integrity": "sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ==", + "dev": true, + "requires": { + "tslib": "~2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, "@babel/code-frame": { "version": "7.0.0-beta.44", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz", @@ -1222,6 +1239,12 @@ "@babel/types": "7.0.0-beta.44" } }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, "@babel/helper-wrap-function": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", @@ -2604,6 +2627,635 @@ "lodash.once": "^4.1.1" } }, + "@endemolshinegroup/cosmiconfig-typescript-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz", + "integrity": "sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==", + "dev": true, + "requires": { + "lodash.get": "^4", + "make-error": "^1", + "ts-node": "^9", + "tslib": "^2" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "@graphql-tools/batch-execute": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-7.0.0.tgz", + "integrity": "sha512-+ywPfK6N2Ddna6oOa5Qb1Mv7EA8LOwRNOAPP9dL37FEhksJM9pYqPSceUcqMqg7S9b0+Cgr78s408rgvurV3/Q==", + "dev": true, + "requires": { + "@graphql-tools/utils": "^7.0.0", + "dataloader": "2.0.0", + "is-promise": "4.0.0", + "tslib": "~2.0.1" + }, + "dependencies": { + "@graphql-tools/utils": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, + "@graphql-tools/delegate": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-7.0.10.tgz", + "integrity": "sha512-6Di9ia5ohoDvrHuhj2cak1nJGhIefJmUsd3WKZcJ2nu2yZAFawWMxGvQImqv3N7iyaWKiVhrrK8Roi/JrYhdKg==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "@graphql-tools/batch-execute": "^7.0.0", + "@graphql-tools/schema": "^7.0.0", + "@graphql-tools/utils": "^7.1.6", + "dataloader": "2.0.0", + "is-promise": "4.0.0", + "tslib": "~2.1.0" + }, + "dependencies": { + "@graphql-tools/utils": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.1.0" + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "@graphql-tools/graphql-file-loader": { + "version": "6.2.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-6.2.7.tgz", + "integrity": "sha512-5k2SNz0W87tDcymhEMZMkd6/vs6QawDyjQXWtqkuLTBF3vxjxPD1I4dwHoxgWPIjjANhXybvulD7E+St/7s9TQ==", + "dev": true, + "requires": { + "@graphql-tools/import": "^6.2.6", + "@graphql-tools/utils": "^7.0.0", + "tslib": "~2.1.0" + }, + "dependencies": { + "@graphql-tools/utils": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.1.0" + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "@graphql-tools/import": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-6.3.0.tgz", + "integrity": "sha512-zmaVhJ3UPjzJSb005Pjn2iWvH+9AYRXI4IUiTi14uPupiXppJP3s7S25Si3+DbHpFwurDF2nWRxBLiFPWudCqw==", + "dev": true, + "requires": { + "resolve-from": "5.0.0", + "tslib": "~2.1.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "@graphql-tools/json-file-loader": { + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-6.2.6.tgz", + "integrity": "sha512-CnfwBSY5926zyb6fkDBHnlTblHnHI4hoBALFYXnrg0Ev4yWU8B04DZl/pBRUc459VNgO2x8/mxGIZj2hPJG1EA==", + "dev": true, + "requires": { + "@graphql-tools/utils": "^7.0.0", + "tslib": "~2.0.1" + }, + "dependencies": { + "@graphql-tools/utils": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, + "@graphql-tools/load": { + "version": "6.2.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-6.2.7.tgz", + "integrity": "sha512-b1qWjki1y/QvGtoqW3x8bcwget7xmMfLGsvGFWOB6m38tDbzVT3GlJViAC0nGPDks9OCoJzAdi5IYEkBaqH5GQ==", + "dev": true, + "requires": { + "@graphql-tools/merge": "^6.2.9", + "@graphql-tools/utils": "^7.5.0", + "globby": "11.0.2", + "import-from": "3.0.0", + "is-glob": "4.0.1", + "p-limit": "3.1.0", + "tslib": "~2.1.0", + "unixify": "1.0.0", + "valid-url": "1.0.9" + }, + "dependencies": { + "@graphql-tools/utils": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.1.0" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "globby": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", + "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "@graphql-tools/merge": { + "version": "6.2.10", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-6.2.10.tgz", + "integrity": "sha512-dM3n37PcslvhOAkCz7Cwk0BfoiSVKXGmCX+VMZkATbXk/0vlxUfNEpVfA5yF4IkP27F04SzFQSaNrbD0W2Rszw==", + "dev": true, + "requires": { + "@graphql-tools/schema": "^7.0.0", + "@graphql-tools/utils": "^7.5.0", + "tslib": "~2.1.0" + }, + "dependencies": { + "@graphql-tools/utils": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.1.0" + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "@graphql-tools/schema": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-7.1.3.tgz", + "integrity": "sha512-ZY76hmcJlF1iyg3Im0sQ3ASRkiShjgv102vLTVcH22lEGJeCaCyyS/GF1eUHom418S60bS8Th6+autRUxfBiBg==", + "dev": true, + "requires": { + "@graphql-tools/utils": "^7.1.2", + "tslib": "~2.1.0" + }, + "dependencies": { + "@graphql-tools/utils": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.1.0" + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "@graphql-tools/url-loader": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-6.8.1.tgz", + "integrity": "sha512-iE/y9IAu0cZYL7o9IIDdGm5WjxacN25nGgVqjZINYlisW/wyuBxng7DMJBAp6yM6gkxkCpMno1ljA/52MXzVPQ==", + "dev": true, + "requires": { + "@graphql-tools/delegate": "^7.0.1", + "@graphql-tools/utils": "^7.1.5", + "@graphql-tools/wrap": "^7.0.4", + "@types/websocket": "1.0.1", + "cross-fetch": "3.0.6", + "eventsource": "1.0.7", + "extract-files": "9.0.0", + "form-data": "4.0.0", + "graphql-upload": "^11.0.0", + "graphql-ws": "4.1.5", + "is-promise": "4.0.0", + "isomorphic-ws": "4.0.1", + "sse-z": "0.3.0", + "sync-fetch": "0.3.0", + "tslib": "~2.1.0", + "valid-url": "1.0.9", + "ws": "7.4.3" + }, + "dependencies": { + "@graphql-tools/utils": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.1.0" + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + }, + "ws": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz", + "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==", + "dev": true + } + } + }, + "@graphql-tools/utils": { + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-6.2.4.tgz", + "integrity": "sha512-ybgZ9EIJE3JMOtTrTd2VcIpTXtDrn2q6eiYkeYMKRVh3K41+LZa6YnR2zKERTXqTWqhobROwLt4BZbw2O3Aeeg==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.1", + "tslib": "~2.0.1" + }, + "dependencies": { + "camel-case": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", + "integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==", + "dev": true, + "requires": { + "pascal-case": "^3.1.1", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, + "@graphql-tools/wrap": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-7.0.5.tgz", + "integrity": "sha512-KCWBXsDfvG46GNUawRltJL4j9BMGoOG7oo3WEyCQP+SByWXiTe5cBF45SLDVQgdjljGNZhZ4Lq/7avIkF7/zDQ==", + "dev": true, + "requires": { + "@graphql-tools/delegate": "^7.0.7", + "@graphql-tools/schema": "^7.1.2", + "@graphql-tools/utils": "^7.2.1", + "is-promise": "4.0.0", + "tslib": "~2.0.1" + }, + "dependencies": { + "@graphql-tools/utils": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", + "dev": true, + "requires": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, "@iam4x/cypress-graphql-mock": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/@iam4x/cypress-graphql-mock/-/cypress-graphql-mock-0.0.1.tgz", @@ -2613,6 +3265,12 @@ "tslib": "^1.9.3" } }, + "@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "dev": true + }, "@jest/console": { "version": "24.7.1", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", @@ -2846,6 +3504,32 @@ "@types/yargs": "^12.0.9" } }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, "@samverschueren/stream-to-observable": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", @@ -3049,6 +3733,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.1.tgz", "integrity": "sha512-aK9jxMypeSrhiYofWWBf/T7O+KwaiAHzM4sveCdWPn71lzUSMimRnKzhXDKfKwV1kWoBo2P1aGgaIYGLf9/ljw==" }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "@types/sinonjs__fake-timers": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", @@ -3079,6 +3769,15 @@ "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, + "@types/websocket": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.1.tgz", + "integrity": "sha512-f5WLMpezwVxCLm1xQe/kdPpQIOmL0TXYx2O15VYfYzc7hTIdxiOoOvez+McSIw3b7z/1zGovew9YSL7+h4h7/Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "12.0.12", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", @@ -3572,6 +4271,12 @@ "readable-stream": "^2.0.6" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -5847,6 +6552,15 @@ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, + "busboy": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", + "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "dev": true, + "requires": { + "dicer": "0.3.0" + } + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -6731,7 +7445,8 @@ "contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=" + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true }, "content-disposition": { "version": "0.5.2", @@ -6876,6 +7591,15 @@ } } }, + "cosmiconfig-toml-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-toml-loader/-/cosmiconfig-toml-loader-1.0.0.tgz", + "integrity": "sha512-H/2gurFWVi7xXvCyvsWRLCMekl4tITJcX0QEsDMpzxtuxDyM59xLatYNg4s/k9AA/HdtCYfj2su8mgA0GSDLDA==", + "dev": true, + "requires": { + "@iarna/toml": "^2.2.5" + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", @@ -6910,6 +7634,21 @@ "sha.js": "^2.4.8" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-fetch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", + "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", + "dev": true, + "requires": { + "node-fetch": "2.6.1" + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -7575,6 +8314,12 @@ } } }, + "dataloader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz", + "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==", + "dev": true + }, "date-fns": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", @@ -7799,6 +8544,15 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=" }, + "dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "dev": true, + "requires": { + "streamsearch": "0.1.2" + } + }, "diff": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", @@ -8352,6 +9106,7 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, "requires": { "debug": "^2.6.9", "resolve": "^1.5.0" @@ -8361,6 +9116,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -8383,6 +9139,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz", "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=", + "dev": true, "requires": { "debug": "^2.6.8", "pkg-dir": "^1.0.0" @@ -8392,6 +9149,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -8400,6 +9158,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -8409,6 +9168,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -8417,16 +9177,35 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, "requires": { "find-up": "^1.0.0" } } } }, + "eslint-plugin-cypress": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.2.tgz", + "integrity": "sha512-1SergF1sGbVhsf7MYfOLiBhdOg6wqyeV9pXUAIDIffYTGMN3dTBQS9nFAzhLsHhO+Bn0GaVM1Ecm71XUidQ7VA==", + "dev": true, + "requires": { + "globals": "^11.12.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, "eslint-plugin-import": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.13.0.tgz", "integrity": "sha512-t6hGKQDMIt9N8R7vLepsYXgDfeuhp6ZJSgtrLEDxonpSubyxUZHjhm6LsAaZX8q6GYVxkbT3kTsV9G5mBCFR6A==", + "dev": true, "requires": { "contains-path": "^0.1.0", "debug": "^2.6.8", @@ -8444,6 +9223,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -8452,6 +9232,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, "requires": { "esutils": "^2.0.2", "isarray": "^1.0.0" @@ -8463,6 +9244,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.2.1.tgz", "integrity": "sha512-xhPXrh0Vl/b7870uEbaumb2Q+LxaEcOQ3kS1jtIXanBAwpMre1l5q/l2l/hESYJGEFKuI78bp6Uw50hlpr7B+g==", + "dev": true, "requires": { "ignore": "^3.3.6", "minimatch": "^3.0.4", @@ -8473,24 +9255,28 @@ "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true } } }, "eslint-plugin-promise": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz", - "integrity": "sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ==" + "integrity": "sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ==", + "dev": true }, "eslint-plugin-standard": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", - "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==" + "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", + "dev": true }, "eslint-plugin-vue": { "version": "4.7.1", "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-4.7.1.tgz", "integrity": "sha512-esETKhVMI7Vdli70Wt4bvAwnZBJeM0pxVX9Yb0wWKxdCJc2EADalVYK/q2FzMw8oKN0wPMdqVCKS8kmR89recA==", + "dev": true, "requires": { "vue-eslint-parser": "^2.0.3" } @@ -8905,6 +9691,12 @@ } } }, + "extract-files": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", + "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==", + "dev": true + }, "extract-from-css": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/extract-from-css/-/extract-from-css-0.4.4.tgz", @@ -8973,6 +9765,83 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -8988,6 +9857,15 @@ "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=" }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", @@ -9279,6 +10157,12 @@ "readable-stream": "^2.0.0" } }, + "fs-capacitor": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-6.2.0.tgz", + "integrity": "sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw==", + "dev": true + }, "fs-extra": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", @@ -10088,6 +10972,91 @@ "iterall": "^1.2.1" } }, + "graphql-config": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-3.2.0.tgz", + "integrity": "sha512-ygEKDeQNZKpm4137560n2oY3bGM0D5zyRsQVaJntKkufWdgPg6sb9/4J1zJW2y/yC1ortAbhNho09qmeJeLa9g==", + "dev": true, + "requires": { + "@endemolshinegroup/cosmiconfig-typescript-loader": "3.0.2", + "@graphql-tools/graphql-file-loader": "^6.0.0", + "@graphql-tools/json-file-loader": "^6.0.0", + "@graphql-tools/load": "^6.0.0", + "@graphql-tools/merge": "^6.0.0", + "@graphql-tools/url-loader": "^6.0.0", + "@graphql-tools/utils": "^6.0.0", + "cosmiconfig": "6.0.0", + "cosmiconfig-toml-loader": "1.0.0", + "minimatch": "3.0.4", + "string-env-interpolation": "1.0.1", + "tslib": "^2.0.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, "graphql-tag": { "version": "2.10.1", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.1.tgz", @@ -10147,6 +11116,64 @@ } } }, + "graphql-upload": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-11.0.0.tgz", + "integrity": "sha512-zsrDtu5gCbQFDWsNa5bMB4nf1LpKX9KDgh+f8oL1288ijV4RxeckhVozAjqjXAfRpxOHD1xOESsh6zq8SjdgjA==", + "dev": true, + "requires": { + "busboy": "^0.3.1", + "fs-capacitor": "^6.1.0", + "http-errors": "^1.7.3", + "isobject": "^4.0.0", + "object-path": "^0.11.4" + }, + "dependencies": { + "http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + } + } + }, + "graphql-ws": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.1.5.tgz", + "integrity": "sha512-yUQ1AjegD1Y9jDS699kyw7Mw+9H+rILm2HoS8N5a5B5YTH93xy3yifFhAJpKGc2wb/8yGdlVy8gTcud0TPqi6Q==", + "dev": true + }, "growl": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", @@ -10651,6 +11678,24 @@ "import-from": "^2.1.0" } }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, "import-from": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", @@ -11127,6 +12172,12 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "dev": true + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -13413,6 +14464,12 @@ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -13543,6 +14600,12 @@ "type-check": "~0.3.2" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "listr": { "version": "0.14.3", "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", @@ -13802,6 +14865,12 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -13942,6 +15011,12 @@ "pify": "^3.0.0" } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "makeerror": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", @@ -14136,6 +15211,12 @@ "readable-stream": "^2.0.1" } }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -14545,6 +15626,12 @@ } } }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + }, "node-forge": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", @@ -14989,6 +16076,12 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" }, + "object-path": { + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.5.tgz", + "integrity": "sha512-jgSbThcoR/s+XumvGMTMf81QVBmah+/Q7K7YduKeKVWL7N111unR2d6pZZarSk6kY/caeNxUDyxOvMWyzoU2eg==", + "dev": true + }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -15278,6 +16371,23 @@ "no-case": "^2.2.0" } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, "parse-asn1": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", @@ -15335,6 +16445,43 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true + } + } + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -15417,6 +16564,12 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -17470,6 +18623,12 @@ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==" }, + "queue-microtask": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz", + "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==", + "dev": true + }, "ramda": { "version": "0.26.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", @@ -18012,6 +19171,12 @@ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -18068,6 +19233,15 @@ "is-promise": "^2.1.0" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -18780,6 +19954,12 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "sse-z": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/sse-z/-/sse-z-0.3.0.tgz", + "integrity": "sha512-jfcXynl9oAOS9YJ7iqS2JMUEHOlvrRAD+54CENiWnc4xsuVLQVSgmwf7cwOTcBd/uq3XkQKBGojgvEtVXcJ/8w==", + "dev": true + }, "sshpk": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", @@ -18888,11 +20068,23 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "dev": true + }, "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, + "string-env-interpolation": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz", + "integrity": "sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==", + "dev": true + }, "string-length": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", @@ -19040,6 +20232,40 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "sync-fetch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.3.0.tgz", + "integrity": "sha512-dJp4qg+x4JwSEW1HibAuMi0IIrBI3wuQr2GimmqB7OXR50wmwzfdusG+p39R9w3R6aFtZ2mzvxvWKQ3Bd/vx3g==", + "dev": true, + "requires": { + "buffer": "^5.7.0", + "node-fetch": "^2.6.1" + }, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + } + } + }, "table": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", @@ -19304,6 +20530,12 @@ "repeat-string": "^1.6.1" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, "topo": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", @@ -19372,6 +20604,38 @@ "tslib": "^1.9.3" } }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, "tsconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", @@ -19436,6 +20700,12 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "typescript": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", + "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "dev": true + }, "uglify-js": { "version": "3.4.6", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.6.tgz", @@ -19610,6 +20880,15 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, + "unixify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz", + "integrity": "sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA=", + "dev": true, + "requires": { + "normalize-path": "^2.1.1" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -19808,6 +21087,12 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, + "valid-url": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", + "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=", + "dev": true + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -19877,6 +21162,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz", "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==", + "dev": true, "requires": { "debug": "^3.1.0", "eslint-scope": "^3.7.1", @@ -20731,6 +22017,12 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, "yargs": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", @@ -20877,6 +22169,18 @@ "fd-slicer": "~1.1.0" } }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, "zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", diff --git a/client/package.json b/client/package.json index d2ebda89..ca766fb2 100644 --- a/client/package.json +++ b/client/package.json @@ -44,11 +44,6 @@ "eslint-config-standard": "^10.2.1", "eslint-friendly-formatter": "^3.0.0", "eslint-loader": "^1.7.1", - "eslint-plugin-import": "^2.7.0", - "eslint-plugin-node": "^5.2.0", - "eslint-plugin-promise": "^3.4.0", - "eslint-plugin-standard": "^3.0.1", - "eslint-plugin-vue": "^4.0.0", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^1.1.4", "friendly-errors-webpack-plugin": "^1.6.1", @@ -112,11 +107,19 @@ "babel-jest": "^24.8.0", "canvas": "^2.5.0", "cypress": "^6.2.1", + "eslint-plugin-cypress": "^2.11.2", + "eslint-plugin-import": "^2.7.0", + "eslint-plugin-node": "^5.2.0", + "eslint-plugin-promise": "^3.4.0", + "eslint-plugin-standard": "^3.0.1", + "eslint-plugin-vue": "^4.0.0", + "graphql-config": "^3.2.0", "jest": "^24.8.0", "jest-serializer-vue": "^2.0.2", "jest-transform-graphql": "^2.1.0", "jest-transform-stub": "^2.0.0", "jest-watch-typeahead": "^0.3.1", + "typescript": "^4.2.3", "vue-jest": "^3.0.4" } } diff --git a/client/src/components/Radiobutton.vue b/client/src/components/Radiobutton.vue index 16b71a78..c807c672 100644 --- a/client/src/components/Radiobutton.vue +++ b/client/src/components/Radiobutton.vue @@ -9,7 +9,7 @@ diff --git a/client/src/components/school-class/CurrentClass.vue b/client/src/components/school-class/CurrentClass.vue index 3b8109cd..a385418b 100644 --- a/client/src/components/school-class/CurrentClass.vue +++ b/client/src/components/school-class/CurrentClass.vue @@ -9,15 +9,6 @@ export default { mixins: [me], - - computed: { - currentClassName() { - let currentClass = this.me.schoolClasses.find(schoolClass => { - return schoolClass.id === this.me.selectedClass.id; - }); - return currentClass ? currentClass.name : this.me.schoolClasses.length ? this.me.schoolClasses[0].name : ''; - } - } }; diff --git a/client/src/components/toggle-menu/ToggleEditing.vue b/client/src/components/toggle-menu/ToggleEditing.vue index a5095ad9..856cc454 100644 --- a/client/src/components/toggle-menu/ToggleEditing.vue +++ b/client/src/components/toggle-menu/ToggleEditing.vue @@ -1,30 +1,29 @@ diff --git a/client/src/components/toggle-menu/ToggleSolutionsForModule.vue b/client/src/components/toggle-menu/ToggleSolutionsForModule.vue index 18f139e8..6c9b466e 100644 --- a/client/src/components/toggle-menu/ToggleSolutionsForModule.vue +++ b/client/src/components/toggle-menu/ToggleSolutionsForModule.vue @@ -7,7 +7,7 @@ + + diff --git a/client/src/graphql/client.js b/client/src/graphql/client.js index ce873a82..2d759392 100644 --- a/client/src/graphql/client.js +++ b/client/src/graphql/client.js @@ -1,4 +1,4 @@ -import {InMemoryCache, defaultDataIdFromObject} from 'apollo-cache-inmemory/lib/index'; +import {InMemoryCache, defaultDataIdFromObject} from 'apollo-cache-inmemory'; import {createHttpLink} from 'apollo-link-http'; import {onError} from 'apollo-link-error'; import {ApolloClient} from 'apollo-client'; diff --git a/client/src/graphql/gql/mutations/syncModuleVisibility.gql b/client/src/graphql/gql/mutations/syncModuleVisibility.gql new file mode 100644 index 00000000..00cd6040 --- /dev/null +++ b/client/src/graphql/gql/mutations/syncModuleVisibility.gql @@ -0,0 +1,5 @@ +mutation SyncModuleVisibility($input: SyncModuleVisibilityInput!) { + syncModuleVisibility(input: $input) { + success + } +} diff --git a/client/src/graphql/typedefs.js b/client/src/graphql/typedefs.js index 4f7c8552..ecad752c 100644 --- a/client/src/graphql/typedefs.js +++ b/client/src/graphql/typedefs.js @@ -1,7 +1,7 @@ import gql from 'graphql-tag'; export const typeDefs = gql` - type SidebarInput { + input SidebarInput { navigation: Boolean profile: Boolean } diff --git a/client/src/mixins/me.js b/client/src/mixins/me.js index aaa043d3..20eb9568 100644 --- a/client/src/mixins/me.js +++ b/client/src/mixins/me.js @@ -5,13 +5,13 @@ export default { return { me: { selectedClass: { - id: '' + id: '', }, permissions: [], schoolClasses: [], - isTeacher: false + isTeacher: false, }, - showPopover: false + showPopover: false, }; }, @@ -21,8 +21,8 @@ export default { return { name: 'topic', params: { - topicSlug: this.me.lastTopic.slug - } + topicSlug: this.me.lastTopic.slug, + }, }; } return '/book/topic/berufliche-grundbildung'; @@ -33,15 +33,23 @@ export default { canManageContent() { return this.me.permissions.includes('users.can_manage_school_class_content'); }, + currentClassName() { + let currentClass = this.me.schoolClasses.find(schoolClass => { + return schoolClass.id === this.me.selectedClass.id; + }); + return currentClass + ? currentClass.name + : (this.me.schoolClasses.length ? this.me.schoolClasses[0].name : ''); + }, }, apollo: { me: { query: ME_QUERY, - update(data) { - return this.$getRidOfEdges(data).me; + update({me}) { + return this.$getRidOfEdges(me); }, - fetchPolicy: 'cache-first' + fetchPolicy: 'cache-first', }, }, }; diff --git a/client/src/pages/module-base.vue b/client/src/pages/module/module-base.vue similarity index 83% rename from client/src/pages/module-base.vue rename to client/src/pages/module/module-base.vue index f2b39298..b6c529c6 100644 --- a/client/src/pages/module-base.vue +++ b/client/src/pages/module/module-base.vue @@ -1,6 +1,6 @@ @@ -11,6 +11,12 @@ export default { components: { ModuleNavigation + }, + + computed: { + showNavigation() { + return !this.$route.meta.hideNavigation; + } } }; diff --git a/client/src/pages/module.vue b/client/src/pages/module/module.vue similarity index 100% rename from client/src/pages/module.vue rename to client/src/pages/module/module.vue diff --git a/client/src/pages/moduleRoom.vue b/client/src/pages/module/moduleRoom.vue similarity index 100% rename from client/src/pages/moduleRoom.vue rename to client/src/pages/module/moduleRoom.vue diff --git a/client/src/pages/module/moduleSettings.vue b/client/src/pages/module/moduleSettings.vue new file mode 100644 index 00000000..4edb2264 --- /dev/null +++ b/client/src/pages/module/moduleSettings.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/client/src/pages/module/moduleVisibility.vue b/client/src/pages/module/moduleVisibility.vue new file mode 100644 index 00000000..2db26c4b --- /dev/null +++ b/client/src/pages/module/moduleVisibility.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/client/src/pages/registration.vue b/client/src/pages/registration.vue index fc1bfdc3..5c426068 100644 --- a/client/src/pages/registration.vue +++ b/client/src/pages/registration.vue @@ -266,7 +266,7 @@ import {register} from '../hep-client/index'; import HELLO_EMAIL from '@/graphql/gql/local/helloEmail.gql'; - import Checkbox from '@/components/Checkbox'; + import Checkbox from '@/components/ui/Checkbox'; import LoadingButton from '@/components/LoadingButton'; import pageTitleMixin from '@/mixins/page-title'; diff --git a/client/src/router/index.js b/client/src/router/index.js index 759df143..b1c58d2a 100644 --- a/client/src/router/index.js +++ b/client/src/router/index.js @@ -1,8 +1,5 @@ import Vue from 'vue'; -// import index from '@/pages/index' import topic from '@/pages/topic'; -import moduleBase from '@/pages/module-base'; -import module from '@/pages/module'; import rooms from '@/pages/rooms'; import room from '@/pages/room'; import newRoom from '@/pages/newRoom'; @@ -10,7 +7,6 @@ import editRoom from '@/pages/editRoom'; import article from '@/pages/article'; import instrument from '@/pages/instrument'; import instrumentOverview from '@/pages/instrumentOverview'; -import submissions from '@/pages/submissions'; import p404 from '@/pages/p404'; import start from '@/pages/start'; import submission from '@/pages/studentSubmission'; @@ -21,7 +17,7 @@ import activity from '@/pages/activity'; import Router from 'vue-router'; import surveyPage from '@/pages/survey'; import styleGuidePage from '@/pages/styleguide'; -import moduleRoom from '@/pages/moduleRoom'; +import moduleRoom from '@/pages/module/moduleRoom'; import login from '@/pages/login'; import betaLogin from '@/pages/beta-login'; import hello from '@/pages/hello'; @@ -40,6 +36,8 @@ import onboardingStart from '@/pages/onboarding/start'; import onboardingStep1 from '@/pages/onboarding/step1'; import onboardingStep2 from '@/pages/onboarding/step2'; import onboardingStep3 from '@/pages/onboarding/step3'; + +import moduleRoutes from './module.routes'; import portfolioRoutes from './portfolio.routes'; import store from '@/store/index'; @@ -52,7 +50,7 @@ const routes = [ { path: '/', name: 'home', - component: start + component: start, }, { path: '/login', @@ -60,8 +58,8 @@ const routes = [ component: login, meta: { layout: 'public', - public: true - } + public: true, + }, }, { path: '/hello', @@ -69,8 +67,8 @@ const routes = [ component: hello, meta: { layout: 'public', - public: true - } + public: true, + }, }, { path: '/beta-login', @@ -78,27 +76,10 @@ const routes = [ component: betaLogin, meta: { layout: 'public', - public: true - } - }, - { - path: '/module/:slug', - component: moduleBase, - children: [ - { - path: '', - name: 'module', - component: module, - meta: {filter: true} - }, - { - path: 'submissions/:id', - name: 'submissions', - component: submissions, - meta: {filter: true} - } - ] + public: true, + }, }, + ...moduleRoutes, {path: '/rooms', name: 'rooms', component: rooms, meta: {filter: true}}, {path: '/new-room/', name: 'new-room', component: newRoom}, {path: '/edit-room/:id', name: 'edit-room', component: editRoom, props: true}, @@ -108,13 +89,13 @@ const routes = [ name: 'moduleRoom', component: moduleRoom, props: true, - meta: {layout: 'fullScreen'} + meta: {layout: 'fullScreen'}, }, {path: '/article/:slug', name: 'article', component: article, meta: {layout: 'simple'}}, { path: '/instruments/', name: 'instrument-overview', - component: instrumentOverview + component: instrumentOverview, }, {path: '/instrument/:slug', name: 'instrument', component: instrument, meta: {layout: 'simple'}}, {path: '/submission/:id', name: 'submission', component: submission, meta: {layout: 'simple'}}, @@ -132,11 +113,11 @@ const routes = [ path: 'old-classes', name: 'old-classes', component: oldClasses, - meta: {isProfile: true} + meta: {isProfile: true}, }, {path: 'create-class', name: 'create-class', component: createClass, meta: {layout: 'simple'}}, {path: 'show-code', name: 'show-code', component: showCode, meta: {layout: 'simple'}}, - ] + ], }, {path: 'join-class', name: 'join-class', component: joinClass, meta: {layout: 'public'}}, { @@ -144,7 +125,7 @@ const routes = [ component: surveyPage, name: 'survey', props: true, - meta: {layout: 'simple'} + meta: {layout: 'simple'}, }, { path: '/register', @@ -153,7 +134,7 @@ const routes = [ meta: { public: true, layout: 'public', - } + }, }, { path: '/check-email', @@ -161,8 +142,8 @@ const routes = [ name: 'checkEmail', meta: { public: true, - layout: 'public' - } + layout: 'public', + }, }, { path: '/verify-email', @@ -170,16 +151,16 @@ const routes = [ name: 'emailVerification', meta: { public: true, - layout: 'public' - } + layout: 'public', + }, }, { path: '/license-activation', component: licenseActivation, name: 'licenseActivation', meta: { - layout: 'public' - } + layout: 'public', + }, }, { path: '/forgot-password', @@ -187,13 +168,13 @@ const routes = [ name: 'forgotPassword', meta: { layout: 'public', - public: true - } + public: true, + }, }, { path: '/news', component: news, - name: 'news' + name: 'news', }, { path: '/onboarding', @@ -205,7 +186,7 @@ const routes = [ name: 'onboarding-start', meta: { layout: 'blank', - next: ONBOARDING_STEP_1 + next: ONBOARDING_STEP_1, }, }, { @@ -215,7 +196,7 @@ const routes = [ meta: { layout: 'blank', next: ONBOARDING_STEP_2, - illustration: 'contents' + illustration: 'contents', }, }, { @@ -225,7 +206,7 @@ const routes = [ meta: { layout: 'blank', next: ONBOARDING_STEP_3, - illustration: 'rooms' + illustration: 'rooms', }, }, { @@ -235,19 +216,19 @@ const routes = [ meta: { layout: 'blank', next: 'home', - illustration: 'portfolio' + illustration: 'portfolio', }, }, - ] + ], }, {path: '/styleguide', component: styleGuidePage}, { path: '*', component: p404, meta: { - layout: 'blank' - } - } + layout: 'blank', + }, + }, ]; Vue.use(Router); @@ -260,7 +241,7 @@ const router = new Router({ return savedPosition; } return {x: 0, y: 0}; - } + }, }); router.afterEach((to, from) => { diff --git a/client/src/router/module.names.js b/client/src/router/module.names.js new file mode 100644 index 00000000..793e0b65 --- /dev/null +++ b/client/src/router/module.names.js @@ -0,0 +1,4 @@ +export const SUBMISSIONS_PAGE = 'submissions'; +export const MODULE_PAGE = 'module'; +export const MODULE_SETTINGS_PAGE = 'module-settings'; +export const VISIBILITY_PAGE = 'visibility'; diff --git a/client/src/router/module.routes.js b/client/src/router/module.routes.js new file mode 100644 index 00000000..ba1d79f1 --- /dev/null +++ b/client/src/router/module.routes.js @@ -0,0 +1,41 @@ +import moduleBase from '@/pages/module/module-base'; +import module from '@/pages/module/module'; +import submissions from '@/pages/submissions'; +import moduleVisibility from '@/pages/module/moduleVisibility'; +import {MODULE_PAGE, MODULE_SETTINGS_PAGE, SUBMISSIONS_PAGE, VISIBILITY_PAGE} from '@/router/module.names'; +import settingsPage from '@/pages/module/moduleSettings'; + +export default [ + { + path: '/module/:slug', + component: moduleBase, + children: [ + { + path: '', + name: MODULE_PAGE, + component: module, + meta: {filter: true}, + }, + { + path: 'submissions/:id', + name: SUBMISSIONS_PAGE, + component: submissions, + meta: {filter: true}, + }, + { + path: 'settings', + name: MODULE_SETTINGS_PAGE, + component: settingsPage, + }, + { + path: 'visibility', + name: VISIBILITY_PAGE, + component: moduleVisibility, + meta: { + layout: 'simple', + hideNavigation: true, + }, + }, + ], + }, +]; diff --git a/client/src/store/index.js b/client/src/store/index.js index 9a243aa2..abd69f39 100644 --- a/client/src/store/index.js +++ b/client/src/store/index.js @@ -204,9 +204,6 @@ export default new Vuex.Store({ setScrollPosition(state, payload) { state.scrollPosition = payload; }, - setNewContentBlock(state, payload) { - state.newContentBlock = payload; - }, setContentBlockPosition(state, payload) { state.contentBlockPosition = payload; }, diff --git a/client/src/styles/_mixins.scss b/client/src/styles/_mixins.scss index dd5aea82..bf627f81 100644 --- a/client/src/styles/_mixins.scss +++ b/client/src/styles/_mixins.scss @@ -211,3 +211,32 @@ line-height: $default-heading-line-height; margin-top: -0.2rem; // to offset the 1.2 line height, it leaves a padding on top } + +@mixin settings-page { + padding: $section-spacing; + + @include desktop { + width: 800px; + display: flex; + flex-direction: column; + justify-self: center; + } + + &__page-title { + @include main-title; + margin-bottom: $section-spacing; + } + + &__heading { + @include heading-1; + margin-bottom: $medium-spacing; + } + + &__paragraph { + @include lead-paragraph; + } + + &__section { + margin-bottom: $section-spacing; + } +} diff --git a/client/src/styles/_variables.scss b/client/src/styles/_variables.scss index 44efdd5d..0ee65d82 100644 --- a/client/src/styles/_variables.scss +++ b/client/src/styles/_variables.scss @@ -55,6 +55,7 @@ $list-height: 52px; $default-border-radius: 13px; $input-border-radius: 3px; +$round-border-radius: 32px; //modal stuff $modal-lateral-padding: 34px; diff --git a/schema.graphql b/schema.graphql new file mode 100644 index 00000000..9d831115 --- /dev/null +++ b/schema.graphql @@ -0,0 +1,1302 @@ +schema { + query: CustomQuery + mutation: CustomMutation +} + +input AddContentBlockInput { + contentBlock: ContentBlockInput + parent: ID + after: ID + clientMutationId: String +} + +type AddContentBlockPayload { + newContentBlock: ContentBlockNode + errors: [String] + clientMutationId: String +} + +input AddNoteArgument { + content: UUID + block: String + type: String + parent: ID + text: String! +} + +input AddNoteInput { + note: AddNoteArgument + clientMutationId: String +} + +type AddNotePayload { + note: NoteNode + clientMutationId: String +} + +input AddObjectiveArgument { + text: String! + objectiveGroup: ID +} + +input AddObjectiveInput { + objective: AddObjectiveArgument + clientMutationId: String +} + +type AddObjectivePayload { + objective: ObjectiveNode + clientMutationId: String +} + +input AddProjectArgument { + title: String + description: String + objectives: String + appearance: String +} + +input AddProjectEntryArgument { + activity: String + reflection: String + nextSteps: String + documentUrl: String + project: ID! +} + +input AddProjectEntryInput { + projectEntry: AddProjectEntryArgument + clientMutationId: String +} + +type AddProjectEntryPayload { + errors: [String] + projectEntry: ProjectEntryNode + clientMutationId: String +} + +input AddProjectInput { + project: AddProjectArgument + clientMutationId: String +} + +type AddProjectPayload { + errors: [String] + project: ProjectNode + clientMutationId: String +} + +input AddRemoveMemberInput { + member: ID! + schoolClass: ID! + active: Boolean! + clientMutationId: String +} + +type AddRemoveMemberPayload { + success: Boolean + clientMutationId: String +} + +input AddRoomArgument { + title: String + description: String + schoolClass: SchoolClassInput + appearance: String +} + +input AddRoomEntryArgument { + title: String! + contents: [ContentElementInput] + room: ID! +} + +input AddRoomEntryInput { + roomEntry: AddRoomEntryArgument + clientMutationId: String +} + +type AddRoomEntryPayload { + roomEntry: RoomEntryNode + errors: [String] + clientMutationId: String +} + +input AddRoomInput { + room: AddRoomArgument + clientMutationId: String +} + +type AddRoomPayload { + errors: [String] + room: RoomNode + clientMutationId: String +} + +type AnswerNode implements Node { + id: ID! + owner: UserNode! + data: JSONString! + survey: SurveyNode! + pk: Int +} + +type AnswerNodeConnection { + pageInfo: PageInfo! + edges: [AnswerNodeEdge]! +} + +type AnswerNodeEdge { + node: AnswerNode + cursor: String! +} + +input AssignmentInput { + id: ID! + answer: String! + document: String + final: Boolean +} + +type AssignmentNode implements Node { + created: DateTime! + modified: DateTime! + id: ID! + title: String! + assignment: String! + solution: String + deleted: Boolean! + owner: UserNode + module: ModuleNode! + userCreated: Boolean! + taskbaseId: String + submissions: [StudentSubmissionNode] + submission: StudentSubmissionNode +} + +type AssignmentNodeConnection { + pageInfo: PageInfo! + edges: [AssignmentNodeEdge]! +} + +type AssignmentNodeEdge { + node: AssignmentNode + cursor: String! +} + +enum BasicKnowledgeType { + LANGUAGE_COMMUNICATION + SOCIETY + INTERDISCIPLINARY +} + +type BookNode implements Node { + title: String! + slug: String! + id: ID! + pk: Int + topics(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): TopicNodeConnection +} + +type BookNodeConnection { + pageInfo: PageInfo! + edges: [BookNodeEdge]! +} + +type BookNodeEdge { + node: BookNode + cursor: String! +} + +type ChapterBookmarkNode implements Node { + user: UserNode! + note: NoteNode + id: ID! + chapter: ChapterNode! +} + +type ChapterBookmarkNodeConnection { + pageInfo: PageInfo! + edges: [ChapterBookmarkNodeEdge]! +} + +type ChapterBookmarkNodeEdge { + node: ChapterBookmarkNode + cursor: String! +} + +type ChapterNode implements Node { + title: String! + slug: String! + description: String! + titleHiddenFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection! + descriptionHiddenFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection! + id: ID! + contentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection + bookmark: ChapterBookmarkNode +} + +type ChapterNodeConnection { + pageInfo: PageInfo! + edges: [ChapterNodeEdge]! +} + +type ChapterNodeEdge { + node: ChapterNode + cursor: String! +} + +type ClassMemberNode { + user: UserNode + active: Boolean + firstName: String + lastName: String + isTeacher: Boolean + id: ID +} + +type ContentBlockBookmarkNode implements Node { + id: ID! + user: UserNode! + note: NoteNode + uuid: UUID + contentBlock: ContentBlockNode! +} + +type ContentBlockBookmarkNodeConnection { + pageInfo: PageInfo! + edges: [ContentBlockBookmarkNodeEdge]! +} + +type ContentBlockBookmarkNodeEdge { + node: ContentBlockBookmarkNode + cursor: String! +} + +input ContentBlockInput { + title: String + type: String + contents: [ContentElementInput] + visibility: [UserGroupBlockVisibility] +} + +type ContentBlockNode implements Node { + title: String! + slug: String! + hiddenFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection! + visibleFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection! + userCreated: Boolean! + contents: GenericStreamFieldType + type: ContentBlockType! + id: ID! + mine: Boolean + bookmarks: [ContentBlockBookmarkNode] +} + +type ContentBlockNodeConnection { + pageInfo: PageInfo! + edges: [ContentBlockNodeEdge]! +} + +type ContentBlockNodeEdge { + node: ContentBlockNode + cursor: String! +} + +enum ContentBlockType { + NORMAL + BASE_COMMUNICATION + TASK + BASE_SOCIETY + BASE_INTERDISCIPLINARY +} + +input ContentElementInput { + id: String + type: InputTypes! + value: ContentElementValueInput +} + +input ContentElementValueInput { + text: String + url: String + description: String + title: String + assignment: String + id: String +} + +input CouponInput { + couponCode: String + clientMutationId: String +} + +type CouponPayload { + success: Boolean + clientMutationId: String +} + +input CreateSchoolClassInput { + name: String + clientMutationId: String +} + +type CreateSchoolClassPayload { + success: Boolean + schoolClass: SchoolClassNode + clientMutationId: String +} + +type CustomMutation { + redeemCoupon(input: CouponInput!): CouponPayload + spellCheck(input: SpellCheckInput!): SpellCheckPayload + addNote(input: AddNoteInput!): AddNotePayload + updateNote(input: UpdateNoteInput!): UpdateNotePayload + updateContentBookmark(input: UpdateContentBookmarkInput!): UpdateContentBookmarkPayload + updateChapterBookmark(input: UpdateChapterBookmarkInput!): UpdateChapterBookmarkPayload + updateModuleBookmark(input: UpdateModuleBookmarkInput!): UpdateModuleBookmarkPayload + updateInstrumentBookmark(input: UpdateInstrumentBookmarkInput!): UpdateInstrumentBookmarkPayload + updateAnswer(input: UpdateAnswerInput!): UpdateAnswerPayload + updatePassword(input: UpdatePasswordInput!): UpdatePasswordPayload + updateAvatar(input: UpdateAvatarInput!): UpdateAvatarPayload + updateSetting(input: UpdateSettingInput!): UpdateSettingPayload + joinClass(input: JoinClassInput!): JoinClassPayload + addRemoveMember(input: AddRemoveMemberInput!): AddRemoveMemberPayload + updateSchoolClass(input: UpdateSchoolClassInput!): UpdateSchoolClassPayload + createSchoolClass(input: CreateSchoolClassInput!): CreateSchoolClassPayload + updateOnboardingProgress: UpdateOnboardingProgress + addProject(input: AddProjectInput!): AddProjectPayload + updateProject(input: UpdateProjectInput!): UpdateProjectPayload + deleteProject(input: DeleteProjectInput!): DeleteProjectPayload + addProjectEntry(input: AddProjectEntryInput!): AddProjectEntryPayload + updateProjectEntry(input: UpdateProjectEntryInput!): UpdateProjectEntryPayload + deleteProjectEntry(input: DeleteProjectEntryInput!): DeleteProjectEntryPayload + updateProjectSharedState(input: UpdateProjectSharedStateInput!): UpdateProjectSharedStatePayload + logout: Logout + coupon(input: CouponInput!): CouponPayload + updateObjectiveVisibility(input: UpdateObjectiveVisibilityInput!): UpdateObjectiveVisibilityPayload + updateObjectiveGroupVisibility(input: UpdateObjectiveGroupVisibilityInput!): UpdateObjectiveGroupVisibilityPayload + addObjective(input: AddObjectiveInput!): AddObjectivePayload + deleteObjective(input: DeleteObjectiveInput!): DeleteObjectivePayload + updateAssignment(input: UpdateAssignmentInput!): UpdateAssignmentPayload + updateSubmissionFeedback(input: UpdateSubmissionFeedbackInput!): UpdateSubmissionFeedbackPayload + updateRoom(input: UpdateRoomInput!): UpdateRoomPayload + addRoom(input: AddRoomInput!): AddRoomPayload + deleteRoom(input: DeleteRoomInput!): DeleteRoomPayload + addRoomEntry(input: AddRoomEntryInput!): AddRoomEntryPayload + deleteRoomEntry(input: DeleteRoomEntryInput!): DeleteRoomEntryPayload + updateRoomEntry(input: UpdateRoomEntryInput!): UpdateRoomEntryPayload + mutateContentBlock(input: MutateContentBlockInput!): MutateContentBlockPayload + addContentBlock(input: AddContentBlockInput!): AddContentBlockPayload + deleteContentBlock(input: DeleteContentBlockInput!): DeleteContentBlockPayload + updateSolutionVisibility(input: UpdateSolutionVisibilityInput!): UpdateSolutionVisibilityPayload + updateLastModule(input: UpdateLastModuleInput!): UpdateLastModulePayload + updateLastTopic(input: UpdateLastTopicInput!): UpdateLastTopicPayload + updateChapterVisibility(input: UpdateChapterVisibilityInput!): UpdateChapterVisibilityPayload + syncModuleVisibility(input: SyncModuleVisibilityInput!): SyncModuleVisibilityPayload + _debug: DjangoDebug +} + +type CustomQuery { + survey(id: ID): SurveyNode + surveys(offset: Int, before: String, after: String, first: Int, last: Int): SurveyNodeConnection + project(id: ID, slug: String): ProjectNode + projects(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, appearance: String): ProjectNodeConnection + instrument(slug: String, id: ID): InstrumentNode + instruments(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, type: String): InstrumentNodeConnection + studentSubmission(id: ID!): StudentSubmissionNode + assignment(id: ID!): AssignmentNode + assignments(offset: Int, before: String, after: String, first: Int, last: Int): AssignmentNodeConnection + node(id: ID!): Node + book(id: ID!): BookNode + topic(slug: String): TopicNode + module(slug: String, id: ID): ModuleNode + chapter(id: ID!): ChapterNode + contentBlock(id: ID!): ContentBlockNode + books(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): BookNodeConnection + topics(before: String, after: String, first: Int, last: Int): TopicConnection + modules(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): ModuleNodeConnection + chapters(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ChapterNodeConnection + objectiveGroup(id: ID!): ObjectiveGroupNode + objectiveGroups(offset: Int, before: String, after: String, first: Int, last: Int, title: String, module_Slug: String): ObjectiveGroupNodeConnection + roomEntry(id: ID, slug: String): RoomEntryNode + room(slug: String, id: ID, appearance: String): RoomNode + rooms(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, appearance: String): RoomNodeConnection + allRoomEntries(offset: Int, before: String, after: String, first: Int, last: Int, slug: String): RoomEntryNodeConnection + moduleRoom(slug: String, classId: ID): RoomNode + me: UserNode + allUsers(offset: Int, before: String, after: String, first: Int, last: Int, username: String, email: String): UserNodeConnection + myActivity(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): ModuleNodeConnection + myInstrumentActivity(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, type: String): InstrumentNodeConnection + _debug: DjangoDebug +} + +scalar DateTime + +input DeleteContentBlockInput { + id: ID! + clientMutationId: String +} + +type DeleteContentBlockPayload { + success: Boolean + errors: String + clientMutationId: String +} + +input DeleteObjectiveInput { + id: ID! + clientMutationId: String +} + +type DeleteObjectivePayload { + success: Boolean + clientMutationId: String +} + +input DeleteProjectEntryInput { + id: ID! + clientMutationId: String +} + +type DeleteProjectEntryPayload { + success: Boolean + errors: [String] + clientMutationId: String +} + +input DeleteProjectInput { + id: ID! + clientMutationId: String +} + +type DeleteProjectPayload { + success: Boolean + errors: [String] + clientMutationId: String +} + +input DeleteRoomEntryInput { + id: ID! + clientMutationId: String +} + +type DeleteRoomEntryPayload { + success: Boolean + roomSlug: String + roomId: ID + errors: [String] + clientMutationId: String +} + +input DeleteRoomInput { + id: ID! + clientMutationId: String +} + +type DeleteRoomPayload { + success: Boolean + errors: [String] + clientMutationId: String +} + +type DjangoDebug { + sql: [DjangoDebugSQL] +} + +type DjangoDebugSQL { + vendor: String! + alias: String! + sql: String + duration: Float! + rawSql: String! + params: String! + startTime: Float! + stopTime: Float! + isSlow: Boolean! + isSelect: Boolean! + transId: String + transStatus: String + isoLevel: String + encoding: String +} + +type FieldError { + code: String +} + +scalar GenericStreamFieldType + +enum InputTypes { + text_block + assignment + image_block + image_url_block + link_block + video_block + document_block +} + +type InstrumentBookmarkNode implements Node { + user: UserNode! + note: NoteNode + id: ID! + uuid: UUID + instrument: InstrumentNode! +} + +type InstrumentNode implements Node { + title: String! + slug: String! + intro: String! + contents: GenericStreamFieldType + type: BasicKnowledgeType! + id: ID! + bookmarks: [InstrumentBookmarkNode] +} + +type InstrumentNodeConnection { + pageInfo: PageInfo! + edges: [InstrumentNodeEdge]! +} + +type InstrumentNodeEdge { + node: InstrumentNode + cursor: String! +} + +scalar JSONString + +input JoinClassInput { + code: String! + clientMutationId: String +} + +type JoinClassPayload { + success: Boolean + schoolClass: SchoolClassNode + clientMutationId: String +} + +type Logout { + success: Boolean +} + +type ModuleBookmarkNode { + user: UserNode! + note: NoteNode + id: ID! + module: ModuleNode! +} + +type ModuleNode implements Node { + title: String! + slug: String! + metaTitle: String! + heroImage: String + teaser: String! + intro: String! + assignments(offset: Int, before: String, after: String, first: Int, last: Int): AssignmentNodeConnection! + objectiveGroups(offset: Int, before: String, after: String, first: Int, last: Int, title: String, module_Slug: String): ObjectiveGroupNodeConnection! + id: ID! + pk: Int + chapters(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ChapterNodeConnection + topic: TopicNode + solutionsEnabled: Boolean + bookmark: ModuleBookmarkNode + mySubmissions(offset: Int, before: String, after: String, first: Int, last: Int): StudentSubmissionNodeConnection + myAnswers(offset: Int, before: String, after: String, first: Int, last: Int): AnswerNodeConnection + myContentBookmarks(offset: Int, before: String, after: String, first: Int, last: Int): ContentBlockBookmarkNodeConnection + myChapterBookmarks(offset: Int, before: String, after: String, first: Int, last: Int): ChapterBookmarkNodeConnection +} + +type ModuleNodeConnection { + pageInfo: PageInfo! + edges: [ModuleNodeEdge]! +} + +type ModuleNodeEdge { + node: ModuleNode + cursor: String! +} + +input MutateContentBlockInput { + id: ID! + contentBlock: ContentBlockInput + clientMutationId: String +} + +type MutateContentBlockPayload { + contentBlock: ContentBlockNode + clientMutationId: String +} + +interface Node { + id: ID! +} + +type NoteNode implements Node { + id: ID! + text: String! + contentblockbookmark: ContentBlockBookmarkNode + modulebookmark: ModuleBookmarkNode + chapterbookmark: ChapterBookmarkNode + instrumentbookmark: InstrumentBookmarkNode + pk: Int +} + +type ObjectiveGroupNode implements Node { + id: ID! + title: ObjectiveGroupTitle + module: ModuleNode! + hiddenFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection! + objectives(offset: Int, before: String, after: String, first: Int, last: Int, text: String): ObjectiveNodeConnection! + pk: Int + displayTitle: String +} + +type ObjectiveGroupNodeConnection { + pageInfo: PageInfo! + edges: [ObjectiveGroupNodeEdge]! +} + +type ObjectiveGroupNodeEdge { + node: ObjectiveGroupNode + cursor: String! +} + +enum ObjectiveGroupTitle { + LANGUAGE_COMMUNICATION + SOCIETY + INTERDISCIPLINARY +} + +type ObjectiveNode implements Node { + id: ID! + text: String! + group: ObjectiveGroupNode! + owner: UserNode + hiddenFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection! + visibleFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection! + order: Int + pk: Int + userCreated: Boolean + mine: Boolean +} + +type ObjectiveNodeConnection { + pageInfo: PageInfo! + edges: [ObjectiveNodeEdge]! +} + +type ObjectiveNodeEdge { + node: ObjectiveNode + cursor: String! +} + +type PageInfo { + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + endCursor: String +} + +input PasswordUpdateInput { + oldPassword: String + newPassword: String +} + +type ProjectEntryNode implements Node { + id: ID! + activity: String! + reflection: String! + nextSteps: String! + documentUrl: String! + created: DateTime! + project: ProjectNode! +} + +type ProjectEntryNodeConnection { + pageInfo: PageInfo! + edges: [ProjectEntryNodeEdge]! +} + +type ProjectEntryNodeEdge { + node: ProjectEntryNode + cursor: String! +} + +type ProjectNode implements Node { + id: ID! + title: String! + description: String + slug: String! + objectives: String! + appearance: String! + student: UserNode! + final: Boolean! + entries(offset: Int, before: String, after: String, first: Int, last: Int): ProjectEntryNodeConnection! + pk: Int + entriesCount: Int +} + +type ProjectNodeConnection { + pageInfo: PageInfo! + edges: [ProjectNodeEdge]! +} + +type ProjectNodeEdge { + node: ProjectNode + cursor: String! +} + +type RoomEntryNode implements Node { + title: String! + description: String + slug: String! + id: ID! + room: RoomNode! + author: UserNode + contents: GenericStreamFieldType + pk: Int +} + +type RoomEntryNodeConnection { + pageInfo: PageInfo! + edges: [RoomEntryNodeEdge]! +} + +type RoomEntryNodeEdge { + node: RoomEntryNode + cursor: String! +} + +type RoomNode implements Node { + title: String! + description: String + slug: String! + id: ID! + schoolClass: SchoolClassNode! + appearance: String! + userCreated: Boolean! + roomEntries(offset: Int, before: String, after: String, first: Int, last: Int, slug: String): RoomEntryNodeConnection! + pk: Int + entryCount: Int +} + +type RoomNodeConnection { + pageInfo: PageInfo! + edges: [RoomNodeEdge]! +} + +type RoomNodeEdge { + node: RoomNode + cursor: String! +} + +input SchoolClassInput { + id: ID + name: String +} + +type SchoolClassNode implements Node { + id: ID! + name: String! + isDeleted: Boolean! + users(offset: Int, before: String, after: String, first: Int, last: Int, username: String, email: String): UserNodeConnection! + code: String + moduleSet(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): ModuleNodeConnection! + hiddenChapterTitles(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ChapterNodeConnection! + hiddenChapterDescriptions(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ChapterNodeConnection! + hiddenContentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection! + visibleContentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection! + hiddenObjectiveGroups(offset: Int, before: String, after: String, first: Int, last: Int, title: String, module_Slug: String): ObjectiveGroupNodeConnection! + hiddenObjectives(offset: Int, before: String, after: String, first: Int, last: Int, text: String): ObjectiveNodeConnection! + visibleObjectives(offset: Int, before: String, after: String, first: Int, last: Int, text: String): ObjectiveNodeConnection! + rooms(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, appearance: String): RoomNodeConnection! + pk: Int + members: [ClassMemberNode] +} + +type SchoolClassNodeConnection { + pageInfo: PageInfo! + edges: [SchoolClassNodeEdge]! +} + +type SchoolClassNodeEdge { + node: SchoolClassNode + cursor: String! +} + +input SpellCheckInput { + text: String! + assignment: ID! + clientMutationId: String +} + +type SpellCheckPayload { + results: [SpellCheckStepNode] + correct: Boolean + clientMutationId: String +} + +type SpellCheckStepNode { + sentence: String + offset: Int + sentenceOffset: Int + length: Int + affected: String + corrected: String +} + +type StudentSubmissionNode implements Node { + created: DateTime! + modified: DateTime! + id: ID! + text: String! + document: String! + assignment: AssignmentNode! + student: UserNode! + final: Boolean! + submissionFeedback: SubmissionFeedbackNode +} + +type StudentSubmissionNodeConnection { + pageInfo: PageInfo! + edges: [StudentSubmissionNodeEdge]! +} + +type StudentSubmissionNodeEdge { + node: StudentSubmissionNode + cursor: String! +} + +input SubmissionFeedbackInput { + id: ID + studentSubmission: ID! + text: String! + final: Boolean +} + +type SubmissionFeedbackNode implements Node { + created: DateTime! + modified: DateTime! + text: String! + teacher: UserNode! + studentSubmission: StudentSubmissionNode! + final: Boolean! + id: ID! +} + +type SurveyNode implements Node { + id: ID! + title: String! + module: ModuleNode + data: JSONString! + answers(offset: Int, before: String, after: String, first: Int, last: Int): AnswerNodeConnection! + pk: Int + answer: AnswerNode +} + +type SurveyNodeConnection { + pageInfo: PageInfo! + edges: [SurveyNodeEdge]! +} + +type SurveyNodeEdge { + node: SurveyNode + cursor: String! +} + +input SyncModuleVisibilityInput { + module: String! + templateSchoolClass: ID! + schoolClass: ID! + clientMutationId: String +} + +type SyncModuleVisibilityPayload { + success: Boolean + clientMutationId: String +} + +type TopicConnection { + pageInfo: PageInfo! + edges: [TopicEdge]! +} + +type TopicEdge { + node: TopicNode + cursor: String! +} + +type TopicNode implements Node { + title: String! + slug: String! + order: Int! + teaser: String! + description: String! + vimeoId: String + instructions: String + id: ID! + pk: Int + modules(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): ModuleNodeConnection +} + +type TopicNodeConnection { + pageInfo: PageInfo! + edges: [TopicNodeEdge]! +} + +type TopicNodeEdge { + node: TopicNode + cursor: String! +} + +scalar UUID + +input UpdateAnswerArgument { + surveyId: ID! + data: String! +} + +input UpdateAnswerInput { + answer: UpdateAnswerArgument + clientMutationId: String +} + +type UpdateAnswerPayload { + answer: AnswerNode + clientMutationId: String +} + +input UpdateAssignmentInput { + assignment: AssignmentInput + clientMutationId: String +} + +type UpdateAssignmentPayload { + updatedAssignment: AssignmentNode + submission: StudentSubmissionNode + successful: Boolean + errors: [String] + clientMutationId: String +} + +input UpdateAvatarInput { + avatarUrl: String + clientMutationId: String +} + +type UpdateAvatarPayload { + success: Boolean + errors: [UpdateError] + clientMutationId: String +} + +input UpdateChapterBookmarkInput { + chapter: ID! + bookmarked: Boolean! + clientMutationId: String +} + +type UpdateChapterBookmarkPayload { + success: Boolean + clientMutationId: String +} + +input UpdateChapterVisibilityInput { + id: ID! + visibility: [UserGroupBlockVisibility] + type: String! + clientMutationId: String +} + +type UpdateChapterVisibilityPayload { + chapter: ChapterNode + clientMutationId: String +} + +input UpdateContentBookmarkInput { + uuid: UUID! + contentBlock: ID! + bookmarked: Boolean! + clientMutationId: String +} + +type UpdateContentBookmarkPayload { + success: Boolean + errors: String + clientMutationId: String +} + +type UpdateError { + field: String + errors: [FieldError] +} + +input UpdateInstrumentBookmarkInput { + uuid: UUID! + instrument: String! + bookmarked: Boolean! + clientMutationId: String +} + +type UpdateInstrumentBookmarkPayload { + success: Boolean + clientMutationId: String +} + +input UpdateLastModuleInput { + id: ID + clientMutationId: String +} + +type UpdateLastModulePayload { + lastModule: ModuleNode + clientMutationId: String +} + +input UpdateLastTopicInput { + id: ID + clientMutationId: String +} + +type UpdateLastTopicPayload { + topic: TopicNode + clientMutationId: String +} + +input UpdateModuleBookmarkInput { + module: String! + bookmarked: Boolean! + clientMutationId: String +} + +type UpdateModuleBookmarkPayload { + success: Boolean + clientMutationId: String +} + +input UpdateNoteArgument { + id: ID! + text: String! +} + +input UpdateNoteInput { + note: UpdateNoteArgument + clientMutationId: String +} + +type UpdateNotePayload { + note: NoteNode + clientMutationId: String +} + +input UpdateObjectiveGroupVisibilityInput { + id: ID! + visibility: [UserGroupBlockVisibility] + clientMutationId: String +} + +type UpdateObjectiveGroupVisibilityPayload { + objectiveGroup: ObjectiveGroupNode + clientMutationId: String +} + +input UpdateObjectiveVisibilityInput { + id: ID! + visibility: [UserGroupBlockVisibility] + clientMutationId: String +} + +type UpdateObjectiveVisibilityPayload { + objective: ObjectiveNode + clientMutationId: String +} + +type UpdateOnboardingProgress { + success: Boolean +} + +input UpdatePasswordInput { + passwordInput: PasswordUpdateInput + clientMutationId: String +} + +type UpdatePasswordPayload { + success: Boolean + errors: [UpdateError] + clientMutationId: String +} + +input UpdateProjectArgument { + title: String + description: String + objectives: String + appearance: String + id: ID! + final: Boolean +} + +input UpdateProjectEntryArgument { + activity: String + reflection: String + nextSteps: String + documentUrl: String + id: ID! +} + +input UpdateProjectEntryInput { + projectEntry: UpdateProjectEntryArgument + clientMutationId: String +} + +type UpdateProjectEntryPayload { + errors: [String] + projectEntry: ProjectEntryNode + clientMutationId: String +} + +input UpdateProjectInput { + project: UpdateProjectArgument + clientMutationId: String +} + +type UpdateProjectPayload { + errors: [String] + project: ProjectNode + clientMutationId: String +} + +input UpdateProjectSharedStateInput { + id: ID + shared: Boolean + clientMutationId: String +} + +type UpdateProjectSharedStatePayload { + success: Boolean + shared: Boolean + errors: [String] + clientMutationId: String +} + +input UpdateRoomArgument { + title: String + description: String + schoolClass: SchoolClassInput + appearance: String + id: ID! +} + +input UpdateRoomEntryArgument { + title: String! + contents: [ContentElementInput] + id: ID! +} + +input UpdateRoomEntryInput { + roomEntry: UpdateRoomEntryArgument + clientMutationId: String +} + +type UpdateRoomEntryPayload { + roomEntry: RoomEntryNode + errors: [String] + clientMutationId: String +} + +input UpdateRoomInput { + room: UpdateRoomArgument + clientMutationId: String +} + +type UpdateRoomPayload { + errors: [String] + room: RoomNode + clientMutationId: String +} + +input UpdateSchoolClassInput { + id: ID! + name: String + clientMutationId: String +} + +type UpdateSchoolClassPayload { + success: Boolean + schoolClass: SchoolClassNode + clientMutationId: String +} + +input UpdateSettingInput { + id: ID! + clientMutationId: String +} + +type UpdateSettingPayload { + success: Boolean + errors: [UpdateError] + clientMutationId: String +} + +input UpdateSolutionVisibilityInput { + slug: String + enabled: Boolean + clientMutationId: String +} + +type UpdateSolutionVisibilityPayload { + success: Boolean + solutionsEnabled: Boolean + errors: [String] + clientMutationId: String +} + +input UpdateSubmissionFeedbackInput { + submissionFeedback: SubmissionFeedbackInput + clientMutationId: String +} + +type UpdateSubmissionFeedbackPayload { + updatedSubmissionFeedback: SubmissionFeedbackNode + successful: Boolean + errors: [String] + clientMutationId: String +} + +input UserGroupBlockVisibility { + schoolClassId: ID! + hidden: Boolean! +} + +type UserNode implements Node { + username: String! + firstName: String! + lastName: String! + lastModule: ModuleNode + lastTopic: TopicNode + avatarUrl: String! + email: String! + onboardingVisited: Boolean! + schoolClasses(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection! + id: ID! + pk: Int + permissions: [String] + selectedClass: SchoolClassNode + expiryDate: String + isTeacher: Boolean + oldClasses(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection + recentModules(offset: Int, before: String, after: String, first: Int, last: Int, recentModules: [ID], orderBy: String): ModuleNodeConnection +} + +type UserNodeConnection { + pageInfo: PageInfo! + edges: [UserNodeEdge]! +} + +type UserNodeEdge { + node: UserNode + cursor: String! +} diff --git a/server/api/schema.py b/server/api/schema.py index f5e87a87..7421800f 100644 --- a/server/api/schema.py +++ b/server/api/schema.py @@ -24,7 +24,6 @@ from rooms.mutations import RoomMutations from rooms.schema import RoomsQuery, ModuleRoomsQuery from users.schema import AllUsersQuery, UsersQuery from users.mutations import ProfileMutations -from registration.mutations_public import RegistrationMutations class CustomQuery(UsersQuery, AllUsersQuery, ModuleRoomsQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, @@ -36,7 +35,7 @@ class CustomQuery(UsersQuery, AllUsersQuery, ModuleRoomsQuery, RoomsQuery, Objec class CustomMutation(BookMutations, RoomMutations, AssignmentMutations, ObjectiveMutations, CoreMutations, PortfolioMutations, - ProfileMutations, SurveyMutations, NoteMutations, RegistrationMutations, SpellCheckMutations, + ProfileMutations, SurveyMutations, NoteMutations, SpellCheckMutations, CouponMutations, graphene.ObjectType): if settings.DEBUG: debug = graphene.Field(DjangoDebug, name='_debug') diff --git a/server/books/models/module.py b/server/books/models/module.py index 159d923b..093a082b 100644 --- a/server/books/models/module.py +++ b/server/books/models/module.py @@ -10,8 +10,6 @@ from books.blocks import DEFAULT_RICH_TEXT_FEATURES from core.wagtail_utils import StrictHierarchyPage from users.models import SchoolClass -logger = logging.getLogger(__name__) - class Module(StrictHierarchyPage): class Meta: @@ -60,6 +58,34 @@ class Module(StrictHierarchyPage): def get_child_ids(self): return self.get_children().values_list('id', flat=True) + def sync_from_school_class(self, school_class_template, school_class_to_sync): + # import here so we don't get a circular import error + from books.models import Chapter, ContentBlock + + # get chapters of module + chapters = Chapter.get_by_parent(self) + content_block_query = ContentBlock.objects.none() + + # get content blocks of chapters + for chapter in chapters: + content_block_query = content_block_query.union(ContentBlock.get_by_parent(chapter)) + + # clear all `hidden for` and `visible for` for class `school_class_to_sync` + for content_block in school_class_to_sync.hidden_content_blocks.intersection(content_block_query): + content_block.hidden_for.remove(school_class_to_sync) + for content_block in school_class_to_sync.visible_content_blocks.intersection(content_block_query): + content_block.visible_for.remove(school_class_to_sync) + + # get all content blocks with `hidden for` for class `school_class_pattern` + for content_block in school_class_template.hidden_content_blocks.intersection(content_block_query): + # add `school_class_to_sync` to these blocks' `hidden for` + content_block.hidden_for.add(school_class_to_sync) + + # get all content blocks with `visible for` for class `school_class_pattern` + for content_block in school_class_template.visible_content_blocks.intersection(content_block_query): + # add `school_class_to_sync` to these blocks' `visible for` + content_block.visible_for.add(school_class_to_sync) + class RecentModule(models.Model): module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='recent_modules') diff --git a/server/books/schema/mutations/__init__.py b/server/books/schema/mutations/__init__.py index fc85ed51..999faf9b 100644 --- a/server/books/schema/mutations/__init__.py +++ b/server/books/schema/mutations/__init__.py @@ -1,6 +1,7 @@ from books.schema.mutations.chapter import UpdateChapterVisibility from books.schema.mutations.contentblock import MutateContentBlock, AddContentBlock, DeleteContentBlock -from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, UpdateLastTopic +from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, SyncModuleVisibility +from books.schema.mutations.topic import UpdateLastTopic class BookMutations(object): @@ -11,3 +12,4 @@ class BookMutations(object): update_last_module = UpdateLastModule.Field() update_last_topic = UpdateLastTopic.Field() update_chapter_visibility = UpdateChapterVisibility.Field() + sync_module_visibility = SyncModuleVisibility.Field() diff --git a/server/books/schema/mutations/module.py b/server/books/schema/mutations/module.py index 77b00244..d50e8b20 100644 --- a/server/books/schema/mutations/module.py +++ b/server/books/schema/mutations/module.py @@ -3,9 +3,10 @@ from datetime import datetime import graphene from graphene import relay -from api.utils import get_errors, get_object -from books.models import Module, Topic, RecentModule -from books.schema.queries import ModuleNode, TopicNode +from api.utils import get_object +from books.models import Module, RecentModule +from books.schema.queries import ModuleNode +from users.models import SchoolClass class UpdateSolutionVisibility(relay.ClientIDMutation): @@ -75,23 +76,30 @@ class UpdateLastModule(relay.ClientIDMutation): return cls(last_module=last_module.module) -class UpdateLastTopic(relay.ClientIDMutation): +class SyncModuleVisibility(relay.ClientIDMutation): class Input: - # todo: use slug here too - id = graphene.ID() + module = graphene.String(required=True) + template_school_class = graphene.ID(required=True) + school_class = graphene.ID(required=True) - topic = graphene.Field(TopicNode) + success = graphene.Boolean() @classmethod def mutate_and_get_payload(cls, root, info, **args): user = info.context.user - id = args.get('id') + if not user.is_teacher(): + raise Exception('Permission denied') - topic = get_object(Topic, id) - if not topic: - raise Topic.DoesNotExist + module_slug = args.get('module') + template_id = args.get('template_school_class') + school_class_id = args.get('school_class') - user.last_topic = topic - user.save() + module = Module.objects.get(slug=module_slug) + template = get_object(SchoolClass, template_id) + school_class = get_object(SchoolClass, school_class_id) + if not template.is_user_in_schoolclass(user) or not school_class.is_user_in_schoolclass(user): + raise Exception('Permission denied') - return cls(topic=topic) + module.sync_from_school_class(template, school_class) + + return cls(success=True) diff --git a/server/books/schema/mutations/topic.py b/server/books/schema/mutations/topic.py new file mode 100644 index 00000000..fafadae1 --- /dev/null +++ b/server/books/schema/mutations/topic.py @@ -0,0 +1,28 @@ +import graphene +from graphene import relay + +from api.utils import get_object +from books.models import Topic +from books.schema.queries import TopicNode + + +class UpdateLastTopic(relay.ClientIDMutation): + class Input: + # todo: use slug here too + id = graphene.ID() + + topic = graphene.Field(TopicNode) + + @classmethod + def mutate_and_get_payload(cls, root, info, **args): + user = info.context.user + id = args.get('id') + + topic = get_object(Topic, id) + if not topic: + raise Topic.DoesNotExist + + user.last_topic = topic + user.save() + + return cls(topic=topic) diff --git a/server/books/schema/queries.py b/server/books/schema/queries.py index 338aae4b..bcf41e0e 100644 --- a/server/books/schema/queries.py +++ b/server/books/schema/queries.py @@ -199,7 +199,7 @@ class TopicNode(DjangoObjectType): class Meta: model = Topic only_fields = [ - 'slug', 'title', 'meta_title', 'teaser', 'description', 'vimeo_id', 'order', 'instructions' + 'slug', 'title', 'teaser', 'description', 'vimeo_id', 'order', 'instructions' ] filter_fields = { 'slug': ['exact', 'icontains', 'in'], diff --git a/server/books/tests/test_copy_visibility_for_other_class.py b/server/books/tests/test_copy_visibility_for_other_class.py new file mode 100644 index 00000000..6fd27a22 --- /dev/null +++ b/server/books/tests/test_copy_visibility_for_other_class.py @@ -0,0 +1,193 @@ +import logging + +from django.test import TestCase, RequestFactory +from graphene.test import Client +from graphql_relay import to_global_id + +from api.schema import schema +from books.models import ContentBlock, Chapter +from books.factories import ModuleFactory +from users.factories import SchoolClassFactory +from users.models import User +from users.services import create_users + +CONTENT_BLOCK_QUERY = """ + query ContentBlockQuery($id: ID!) { + contentBlock(id: $id) { + hiddenFor { + edges { + node { + id + name + } + } + } + visibleFor { + edges { + node { + id + name + } + } + } + } + } + """ + +SYNC_MUTATION = """ +mutation SyncMutationVisibility($input: SyncModuleVisibilityInput!) { + syncModuleVisibility(input: $input) { + success + } +} +""" + + +class CopyVisibilityForClassesTestCase(TestCase): + """ + what do we want to happen? + we have 3 public content blocks [X, Y, Z] + we have 2 custom content block [M, N] + we have 2 school classes [A, B] + one public content block is hidden for class A | [X, Y] + one custom content block is visible for class A | [M] + class B also sees two of three public content blocks, but one is different from what A sees | [X, Z] + class B doesn't see the custom content block, but another one | [N] + so A sees | [X, Y, M] + B sees | [X, Z, N] + we want to copy the settings from class A to class B + now class B sees the same content blocks as class A | [X, Y, N] + """ + + def setUp(self): + module = ModuleFactory(slug='some-module') + chapter = Chapter(title='Some Chapter') + module.add_child(instance=chapter) + create_users() + teacher = User.objects.get(username='teacher') + student1 = User.objects.get(username='student1') + student2 = User.objects.get(username='student2') + # school class to be used as the pattern or model + template_school_class = SchoolClassFactory(name='template-class', users=[teacher, student1]) + # school class to be synced, e.g. adapted to be like the other + school_class_to_be_synced = SchoolClassFactory(name='class-to-be-synced', users=[teacher, student2]) + + default_content_block = ContentBlock(title='default block', slug='default') + hidden_content_block = ContentBlock(title='hidden block', slug='hidden') + other_hidden_content_block = ContentBlock(title='other hidden block', slug='other-hidden') + custom_content_block = ContentBlock(title='custom block', slug='custom', owner=teacher) + other_custom_content_block = ContentBlock(title='other custom block', slug='other-custom', owner=teacher) + + chapter.specific.add_child(instance=default_content_block) + chapter.specific.add_child(instance=hidden_content_block) + chapter.specific.add_child(instance=custom_content_block) + chapter.specific.add_child(instance=other_custom_content_block) + chapter.specific.add_child(instance=other_hidden_content_block) + + hidden_content_block.hidden_for.add(template_school_class) + custom_content_block.visible_for.add(template_school_class) + + other_hidden_content_block.hidden_for.add(school_class_to_be_synced) + other_custom_content_block.visible_for.add(school_class_to_be_synced) + + teacher_request = RequestFactory().get('/') + teacher_request.user = teacher + self.teacher_client = Client(schema=schema, context_value=teacher_request) + + student1_request = RequestFactory().get('/') + student1_request.user = student1 + self.student1_client = Client(schema=schema, context_value=student1_request) + + student2_request = RequestFactory().get('/') + student2_request.user = student2 + self.student2_client = Client(schema=schema, context_value=student2_request) + + self.template_school_class = template_school_class + self.school_class_to_be_synced = school_class_to_be_synced + self.module = module + self.chapter = to_global_id('ChapterNode', chapter.pk) + self.default_content_block = to_global_id('ContentBlockNode', default_content_block.pk) + self.hidden_content_block = to_global_id('ContentBlockNode', hidden_content_block.pk) + self.custom_content_block = to_global_id('ContentBlockNode', custom_content_block.pk) + self.other_custom_content_block = to_global_id('ContentBlockNode', other_custom_content_block.pk) + self.other_hidden_content_block = to_global_id('ContentBlockNode', other_hidden_content_block.pk) + + def _get_result(self, query, client, id): + result = client.execute(query, variables={ + 'id': id + }) + return result + + def _test_in_sync(self): + # the hidden block is hidden for both now + hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.hidden_content_block) + hidden_for = hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges') + self.assertTrue('template-class' in map(lambda x: x['node']['name'], hidden_for)) + self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], hidden_for)) + + # the other hidden block is hidden for no one now + other_hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, + self.other_hidden_content_block) + hidden_for = other_hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges') + self.assertEqual(len(hidden_for), 0) + + # the default block is still hidden for no one + default_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.default_content_block) + hidden_for = default_result.get('data').get('contentBlock').get('hiddenFor').get('edges') + self.assertEqual(len(hidden_for), 0) + + # the custom block is visible for both + custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.custom_content_block) + visible_for = custom_result.get('data').get('contentBlock').get('visibleFor').get('edges') + self.assertTrue('template-class' in map(lambda x: x['node']['name'], visible_for)) + self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], visible_for)) + + # the other custom block is visible for no one + other_custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, + self.other_custom_content_block) + visible_for = other_custom_result.get('data').get('contentBlock').get('visibleFor').get('edges') + self.assertEqual(len(visible_for), 0) + + def test_hidden_for_and_visible_for_set_correctly(self): + self.assertEqual(ContentBlock.objects.count(), 5) + + hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.hidden_content_block) + hidden_for = hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges') + self.assertTrue('template-class' in map(lambda x: x['node']['name'], hidden_for)) + self.assertFalse('class-to-be-synced' in map(lambda x: x['node']['name'], hidden_for)) + + other_hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, + self.other_hidden_content_block) + hidden_for = other_hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges') + self.assertFalse('template-class' in map(lambda x: x['node']['name'], hidden_for)) + self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], hidden_for)) + + default_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.default_content_block) + hidden_for = default_result.get('data').get('contentBlock').get('hiddenFor').get('edges') + self.assertEqual(len(hidden_for), 0) + + custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.custom_content_block) + visible_for = custom_result.get('data').get('contentBlock').get('visibleFor').get('edges') + self.assertTrue('template-class' in map(lambda x: x['node']['name'], visible_for)) + self.assertFalse('class-to-be-synced' in map(lambda x: x['node']['name'], visible_for)) + + other_custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, + self.other_custom_content_block) + visible_for = other_custom_result.get('data').get('contentBlock').get('visibleFor').get('edges') + self.assertFalse('template-class' in map(lambda x: x['node']['name'], visible_for)) + self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], visible_for)) + + def test_syncs_correctly(self): + self.module.sync_from_school_class(self.template_school_class, self.school_class_to_be_synced) + + self._test_in_sync() + + def test_mutation(self): + self.teacher_client.execute(SYNC_MUTATION, variables={ + 'input': { + 'module': self.module.slug, + 'templateSchoolClass': to_global_id('SchoolClassNode', self.template_school_class.pk), + 'schoolClass': to_global_id('SchoolClassNode', self.school_class_to_be_synced.pk) + } + }) + self._test_in_sync() diff --git a/server/core/settings.py b/server/core/settings.py index 74892f06..96f28268 100644 --- a/server/core/settings.py +++ b/server/core/settings.py @@ -335,6 +335,11 @@ LOGGING = { 'level': 'WARNING', 'propagate': True, }, + 'wagtail': { + 'handlers': ['console'], + 'level': 'WARNING', + 'propagate': False, + }, } } @@ -348,25 +353,6 @@ if not DEBUG and os.environ.get('SENTRY_DSN'): send_default_pii=True ) - # LOGGING['handlers'] = { - # 'sentry': { - # 'level': 'ERROR', # ERROR, WARNING, INFO - # 'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler', - # 'tags': {'custom-tag': 'x'}, - # }, - # 'console': { - # 'level': 'INFO', - # 'class': 'logging.StreamHandler', - # 'stream': sys.stdout, - # 'formatter': 'simple_format' - # } - # } - # - # for k, v in LOGGING['loggers'].items(): - # LOGGING['loggers'][k]['handlers'] = ['sentry', 'console'] -# else: -# RAVEN_CONFIG = {} - RAVEN_DSN_JS = os.environ.get('RAVEN_DSN_JS', '') GOOGLE_TAG_MANAGER_CONTAINER_ID = os.environ.get('GOOGLE_TAG_MANAGER_CONTAINER_ID') diff --git a/server/graphql.config.json b/server/graphql.config.json deleted file mode 100644 index 8208188c..00000000 --- a/server/graphql.config.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "schema": { - "request": { - "url": "http://localhost:8000/graphql", - "method": "POST", - "postIntrospectionQuery": true, - "options": { - "headers": { - "user-agent": "JS GraphQL" - } - } - } - }, - "endpoints": [ - { - "name": "Default (http://localhost:8000/graphql", - "url": "http://localhost:8000/graphql", - "options": { - "headers": { - "user-agent": "JS GraphQL" - } - } - } - ] -} \ No newline at end of file diff --git a/server/registration/tests/test_registration.py b/server/registration/tests/test_registration.py index 4baf4f2b..cab50142 100644 --- a/server/registration/tests/test_registration.py +++ b/server/registration/tests/test_registration.py @@ -13,7 +13,7 @@ from django.contrib.sessions.middleware import SessionMiddleware from django.test import TestCase, RequestFactory from graphene.test import Client -from api.schema import schema +from api.schema_public import schema from core.hep_client import HepClient from core.tests.mock_hep_data_factory import ME_DATA, VALID_TEACHERS_ORDERS from users.models import License