├── .gitattributes ├── .github ├── beispiel_layout.pdf └── img │ ├── community_checklist.png │ ├── create_pr.png │ ├── css-frameworks-comparison.png │ ├── layout1.png │ ├── layout2.png │ ├── layout3.png │ ├── scenario1.png │ ├── scenario2.png │ ├── scenario3.png │ └── watch_repo.png ├── .gitignore ├── .hello-encrypted-world ├── README.md ├── archive ├── 1 │ ├── README.md │ ├── components │ │ ├── List.js │ │ └── ListItem.js │ ├── exercise-vuejs.gif │ ├── index.html │ └── index.js ├── 2 │ ├── README.md │ ├── build-server-badge.png │ ├── build-server-checks.png │ ├── code-suggestions.png │ └── terminal.png ├── 3 │ ├── README.md │ ├── crud.png │ └── permissions.png ├── 4 │ └── README.md ├── 5 │ └── README.md ├── 6 │ └── README.md └── Exam │ ├── .gitignore │ ├── code │ ├── css │ │ └── example.html │ ├── graphql │ │ ├── index.js │ │ └── package.json │ ├── review │ │ ├── apollo-server │ │ │ ├── index.js │ │ │ ├── resolvers.js │ │ │ └── typedefs.js │ │ └── component-tests │ │ │ ├── 1.js │ │ │ └── 2.js │ └── vuejs │ │ ├── Example.vue │ │ └── package.json │ ├── exam.pdf │ ├── exam.tex │ ├── git │ └── initial.svg │ ├── neo4j │ ├── after-merge.svg │ └── before-merge.svg │ └── project-management │ └── software-testing-benefits.svg └── exercises ├── 1 ├── README.md └── images │ ├── demo.gif │ └── hackernews.png ├── 2 ├── README.md └── images │ ├── storybook-a11y-example.png │ └── storybook-controls-example.png ├── 3 └── README.md ├── 4 └── README.md ├── 5 └── README.md ├── 6 └── README.md ├── 7 └── README.md ├── 8 └── README.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | /.hello-encrypted-world filter=git-crypt diff=git-crypt 2 | 3 | -------------------------------------------------------------------------------- /.github/beispiel_layout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/beispiel_layout.pdf -------------------------------------------------------------------------------- /.github/img/community_checklist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/community_checklist.png -------------------------------------------------------------------------------- /.github/img/create_pr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/create_pr.png -------------------------------------------------------------------------------- /.github/img/css-frameworks-comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/css-frameworks-comparison.png -------------------------------------------------------------------------------- /.github/img/layout1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/layout1.png -------------------------------------------------------------------------------- /.github/img/layout2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/layout2.png -------------------------------------------------------------------------------- /.github/img/layout3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/layout3.png -------------------------------------------------------------------------------- /.github/img/scenario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/scenario1.png -------------------------------------------------------------------------------- /.github/img/scenario2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/scenario2.png -------------------------------------------------------------------------------- /.github/img/scenario3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/scenario3.png -------------------------------------------------------------------------------- /.github/img/watch_repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.github/img/watch_repo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | git-crypt-key 2 | -------------------------------------------------------------------------------- /.hello-encrypted-world: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/.hello-encrypted-world -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Systems Development and Frameworks - 2020/21 2 | 3 | This is the homework repository of course `Systems Development and Frameworks` 4 | at `Hochschule für Technik und Wirtschaft` in Berlin. 5 | 6 | 7 |

8 | homework 9 |

10 | 11 | ## Homework - Exercise #0 12 | 13 | 1. Find team members to groups of 2-3 people. Together, come up with a team name (Team names must be url-safe). We will setup a team on Github and Moodle for you. Kudos for a team name with a corresponding emoji. 14 | 2. Make a copy of this repository (using a second remote). Ensure that each member of your team has sufficient access. :star: 15 | 3. Replace the content of this `README.md` with your individual content. Here's a Github [README.md template](https://github.com/othneildrew/Best-README-Template) and there are more [awesome READMEs](https://github.com/matiassingers/awesome-readme) out there. :star: 16 | 17 | You can also have a look on the community checklist to get inspiration: 18 | 19 | 20 | ![community checklist](./.github/img/community_checklist.png) 21 | 22 | ### How to submit homework 23 | 24 | You have to create a feature branch first, commit your code and push it to your 25 | copy of the homework repository. Then create a pull request with your feature 26 | branch as the target branch and request a review from your teachers. 27 | 28 | ![create pull request](./.github/img/create_pr.png) 29 | 30 | Make sure that you *watch* the original homework repository. That's where we 31 | update exercise descriptions. Keep your own copy up-to-date by merging in new 32 | commits of the original repository. 33 | 34 | ![watch home repository](./.github/img/watch_repo.png) 35 | 36 | Do not submit lengthy pull request. Pull requests with a large `diff` in 37 | `Files changed` tab are difficult to review. If you want to get good feedback on 38 | your pull requests, don't push unrelated changes. 39 | -------------------------------------------------------------------------------- /archive/1/README.md: -------------------------------------------------------------------------------- 1 | # Exercise \#1 2 | 3 | Implement a simple todo app with VueJS. 4 | 5 | Each item in the todo app should: 6 | 7 | * Edit: display a form to update the todo 8 | * Save: update the todo and display it 9 | * Cancel: cancel the form submission 10 | * Delete: delete the todo 11 | 12 | Here is how the result should look like: 13 | 14 | ![screencast](./exercise-vuejs.gif) 15 | 16 | Use computed properties and events where possible and make use of the component 17 | pattern. 18 | 19 | Get extra praise for implementing the "Add todo" button! :heart: 20 | -------------------------------------------------------------------------------- /archive/1/components/List.js: -------------------------------------------------------------------------------- 1 | export default Vue.component('list', {}) 2 | -------------------------------------------------------------------------------- /archive/1/components/ListItem.js: -------------------------------------------------------------------------------- 1 | export default Vue.component('list-item', {}) 2 | -------------------------------------------------------------------------------- /archive/1/exercise-vuejs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/archive/1/exercise-vuejs.gif -------------------------------------------------------------------------------- /archive/1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

5 | 6 |
7 | 8 | 9 | -------------------------------------------------------------------------------- /archive/1/index.js: -------------------------------------------------------------------------------- 1 | import List from './components/List.js' 2 | import ListItem from './components/ListItem.js' 3 | 4 | new Vue({ 5 | el: '#app', 6 | data: { 7 | todos: [ 8 | { id: '1', message: 'Foo', }, 9 | { id: '2', message: 'Bar', }, 10 | { id: '3', message: 'Baz', } 11 | ] // feel free to put the `todos` array where you find it most suitable 12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /archive/2/README.md: -------------------------------------------------------------------------------- 1 | # Exercise \#2 2 | 3 | Write test for your existing code. 4 | 5 | This is not exactly test driven development, because you do the implementation 6 | first and add the automated tests later. Don't worry, we keep the test driven 7 | development for the upcoming exercises. However, do mutant testing in order to 8 | double-check if your tests actually fail for a bug in your code. 9 | 10 | 1. Add a linter to your project, e.g. `eslint` or `prettier`. 11 | 2. Test your components with `jest` and `vue-test-utils`. Here is some 12 | inspiration how the tests could be structured: 13 | ![Terminal output](./terminal.png) 14 | 3. Setup a build server. If your linter or your tests fail, that should fail the 15 | build. Popular choices for open source projects are [Travis CI](https://travis-ci.com/) 16 | or [Circle CI](https://circleci.com/) 17 | 4. Integrate your build server status into your repository, i.e. your build 18 | server tests each of your pull requests and you can see the result on the 19 | pull request. 20 | ![Github checks](./build-server-checks.png) 21 | You can also add a badge to your `README.md`. 22 | ![Build server badge](./build-server-badge.png) 23 | 5. Request a review from @roschaefer 24 | 6. Request a review from a member of another team 25 | 7. Review a pull request of another group. Either "Request Changes" or 26 | "Approve". Give suggestions by clicking on the line of code that needs 27 | improvement. 28 | ![Give code suggestions](./code-suggestions.png) 29 | 30 | 31 | If you copy code from other groups, please give credit to them in your commit 32 | messages. 33 | -------------------------------------------------------------------------------- /archive/2/build-server-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/archive/2/build-server-badge.png -------------------------------------------------------------------------------- /archive/2/build-server-checks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/archive/2/build-server-checks.png -------------------------------------------------------------------------------- /archive/2/code-suggestions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/archive/2/code-suggestions.png -------------------------------------------------------------------------------- /archive/2/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/archive/2/terminal.png -------------------------------------------------------------------------------- /archive/3/README.md: -------------------------------------------------------------------------------- 1 | # Exercise \#3 2 | 3 | Test-drive the development of a GraphQL server. 4 | 5 | **Deadline is December 4th, 2019** 6 | 7 | ### tl;dr 8 | 9 | 1. Show that you can can setup an [apollo-server](https://www.apollographql.com/docs/apollo-server/). 10 | 2. Test your backend with [apollo-server-testing](https://www.apollographql.com/docs/apollo-server/testing/testing/). 11 | 3. Implement [JWT](https://jwt.io/) and have at least one query which requires authentication. 12 | 4. Add backend testing to your build server pipeline. 13 | 5. Request a review from @roschaefer 14 | 6. Request a review from sb. else 15 | 16 | Note that it is **not required** that you connect your frontend with your 17 | backend. 18 | 19 | 20 | ### Suggested workflow and some inspiration 21 | 22 | * Test-drive your implementation. As a first step, you can just write down the 23 | documentation, e.g. 24 | ```js 25 | it.todo("behaves in a specified way") 26 | ``` 27 | will create a to-be-implemented test case with this description. 28 | * Pro tip: I run queries and mutations against an apollo server and check the 29 | response with jest's [`toMatchObject`](https://jestjs.io/docs/en/expect#tomatchobjectobject) 30 | matcher all the time. You can use it to check for both `data` and `errors`. 31 | * Have a look into the [Human Connection](https://github.com/human-connection/human-connection) 32 | repo to learn how we implement JWT and write backend tests. 33 | * Be creative with your type definitions in your schema and have some custom 34 | types. You could e.g. implement CRUD operations for your types. 35 | * If you want to connect your frontend with your backend (totally optional) it 36 | would be wise if one of your types is a `Todo`. 37 | * Here is some inspiration: ![Test cases for CRUD operations](./crud.png) 38 | 39 | 40 | ### Optional (will be part of an upcoming exercise) 41 | * Implement a permission layer around your app. Use [graphql-shield](https://github.com/maticzav/graphql-shield) 42 | and [graphql-middleware](https://github.com/prisma-labs/graphql-middleware). 43 | * Here is some inspiration how your test cases could look like: 44 | ![Test cases for a permission layer](./permissions.png) 45 | * Connect your frontend with your backend via [vue-apollo](https://github.com/vuejs/vue-apollo). 46 | So if you update a todo in your frontend, it sends a graphql mutation to the 47 | backend. 48 | 49 | 50 | If you copy code from other groups, please give credit to them in your commit 51 | messages. 52 | -------------------------------------------------------------------------------- /archive/3/crud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/archive/3/crud.png -------------------------------------------------------------------------------- /archive/3/permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/archive/3/permissions.png -------------------------------------------------------------------------------- /archive/4/README.md: -------------------------------------------------------------------------------- 1 | # Exercise \#4 2 | 3 | Connect your database with Neo4J 4 | 5 | **Deadline is December 18th, 2019** 6 | 7 | 1. Refactor your backend so that all the data is stored in[Neo4J](https://neo4j.com/). 8 | 2. You are allowed to use a query builder like [neo4j-graphql-js](https://github.com/neo4j-graphql/neo4j-graphql-js) 9 | but at least one of your mutations and queries should access the database 10 | directly with [Neo4j JS driver](https://github.com/neo4j/neo4j-javascript-driver) 11 | and a custom cypher statement. 12 | 3. Your objects in the database should be connected in some way. If you have a 13 | relationship like 14 | ``` 15 | (:User)<-[:ASSIGNED]-(:Todo) 16 | ``` 17 | then this query should return todos and user objects: 18 | ```gql 19 | query { 20 | todos { 21 | assignedTo { 22 | name 23 | } 24 | } 25 | } 26 | ``` 27 | 4. Implement a filter (`WHERE` in cypher). 28 | 5. Implement some ordering (`ORDER BY` in cypher). 29 | 6. Implement pagination (`SKIP` and `LIMIT` in cypher). 30 | 7. Implement an update mutation that uses `MERGE`. 31 | 8. Write backend tests for all of the above. 32 | 9. Request a review from @roschaefer. 33 | 10. Request a review from sb. else. 34 | 35 | If you copy code from other groups, please give credit to them in your commit 36 | messages. 37 | -------------------------------------------------------------------------------- /archive/5/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Exercise \#5 3 | 4 | Learn how to use graphql-middlewares and start programming with NuxtJS. 5 | 6 | **Deadline is January 8th, 2020** 7 | 8 | 1. Optional tasks of exercise #3 are now required: Implement a permission 9 | layer around your app. Use [graphql-shield](https://github.com/maticzav/graphql-shield) 10 | and [graphql-middleware](https://github.com/prisma-labs/graphql-middleware). 11 | Here is some inspiration how your test cases could look like: 12 | ![Test cases for a permission layer](../3/permissions.png) 13 | 2. Refactor your backend and frontend to show something different than just 14 | todos and users. Be creative. 15 | 3. Do a remote pair-programming session. You can choose any pairing partner, 16 | either from our course or you can also ask our open-source community. 17 | 4. Record your pair-programming session and publish it. Choose any software you 18 | want. If you don't like being on the web, disable your webcam and save the 19 | video as "unlisted". I recommend [PeerTube](https://joinpeertube.org/) to 20 | host the video but there is also this commercial platform called YouTube as 21 | an alternative. Send a link to your recorded video to htw@roschaefer.de. 22 | 5. Write backend tests. 23 | 6. Request a review from @roschaefer. 24 | 7. Request a review from sb. else. 25 | 26 | ### Teaching goal 27 | 28 | The point of exercise 3. and 4. is to socialize and see how easy it is to find 29 | people from around the world who like to program with you and learn with you. 30 | Even a recording of a pairing session can be interesting learning material. 31 | Think of people who don't have access to public education, e.g. studying at a 32 | university like you do. 33 | 34 | Also, if you get stuck, people can help out. Often, developers wait for too long 35 | before they ask for help. It causes a lot of frustration and this particular 36 | situation happened for a team while working on exercise \#3. Asking for help in 37 | our community chat is quick and usually there is always somebody around who can 38 | help out: https://human-connection.org/discord 39 | 40 | ### Optional exercises 41 | 42 | 1. Create a NuxtJS app and write a couple of different page components. Your 43 | page component should have some level of nesting. Like `/nested.vue`, 44 | `/nested/index.vue` and `/nested/_id.vue`. 45 | 2. Connect your frontend with your backend via [apollo-module](https://github.com/nuxt-community/apollo-module). 46 | So if you update a data object in your frontend, it sends a graphql mutation 47 | to the backend. 48 | 3. Make use of apollo-module's [authentication helpers](https://github.com/nuxt-community/apollo-module#authentication) 49 | and have at least one page component which requires authentication. 50 | 4. Write frontend tests, mock `this.$apollo` and respond with some mocked data or simulate an error. 51 | 52 | 53 | If you copy code from other groups, please give credit to them in your commit 54 | messages. 55 | -------------------------------------------------------------------------------- /archive/6/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Exercise \#6 3 | 4 | Start programming with NuxtJS and learn fullstack testing. 5 | 6 | **Deadline is January 22th, 2020** 7 | 8 | 1. Create a NuxtJS app and write a couple of different page components. Your 9 | page component should have some level of nesting. Like `/nested.vue`, 10 | `/nested/index.vue` and `/nested/_id.vue`. 11 | 2. Connect your frontend with your backend via [apollo-module](https://github.com/nuxt-community/apollo-module). 12 | So if you update a data object in your frontend, it sends a graphql mutation 13 | to the backend. 14 | 3. Make use of apollo-module's [authentication helpers](https://github.com/nuxt-community/apollo-module#authentication). 15 | 4. Implement a page component which requires authentication. Make sure that your 16 | frontend returns a HTTP status code 403 if you are not allowed to see that 17 | page. 18 | 5. Request a review from @roschaefer. 19 | 6. Request a review from sb. else. 20 | 21 | ### How to get help 22 | 23 | If you have troubles with the setup or dependencies, feel free to get in touch 24 | with the open-source community: https://human-connection.org/discord 25 | Most contributors know NuxtJS very well and can help with the setup. 26 | 27 | 28 | ### Optional exercises 29 | 30 | 1. Write frontend tests, mock `this.$apollo` and respond with some mocked data 31 | or simulate an error. 32 | 2. Write a fullstack test with https://www.cypress.io/. 33 | 3. Record and publish a pair-programming :heart_eyes: 34 | 35 | If you copy code from other groups, please give credit to them in your commit 36 | messages. 37 | -------------------------------------------------------------------------------- /archive/Exam/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.aux 3 | svg-inkscape/* 4 | code/graphql/node_modules/ 5 | code/vuejs/node_modules/ 6 | 7 | -------------------------------------------------------------------------------- /archive/Exam/code/css/example.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /archive/Exam/code/graphql/index.js: -------------------------------------------------------------------------------- 1 | const { ApolloServer, gql } = require('apollo-server') 2 | 3 | const typeDefs = gql` 4 | type Student { 5 | id: ID 6 | firstname: String 7 | lastname: String 8 | fullname(reverse: Boolean): String 9 | } 10 | 11 | type Query { 12 | student(id: ID): Student 13 | allStudents: [Student] 14 | } 15 | ` 16 | const students = [ 17 | { id: '1', firstname: 'Bob', lastname: 'Builder' }, 18 | { id: '2', firstname: 'Alice', lastname: 'Wonderland' }, 19 | ] 20 | 21 | const resolvers = { 22 | Query: { 23 | student: (parent, args, context, resolveInfo) => { 24 | console.log('Query.student:', parent, args) 25 | return students.find(student => student.id === args.id) 26 | }, 27 | allStudents: (parent, args, context, resolveInfo) => { 28 | console.log('Query.allStudents:', parent, args) 29 | return students 30 | }, 31 | }, 32 | Student: { 33 | fullname: (parent, args, context, resolveInfo) => { 34 | console.log('Student.fullname:', parent.id, args) 35 | if (args.reverse) return [parent.lastname, parent.firstname].join(', ') 36 | return [parent.firstname, parent.lastname].join(' ') 37 | } 38 | } 39 | } 40 | 41 | const server = new ApolloServer({ typeDefs, resolvers }); 42 | server.listen().then(({ url }) => { 43 | console.log(`🚀 Server ready at ${url}`); 44 | }); 45 | -------------------------------------------------------------------------------- /archive/Exam/code/graphql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exam-apollo-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "apollo-server": "^2.19.0" 8 | }, 9 | "devDependencies": {}, 10 | "scripts": { 11 | "start": "node index.js" 12 | }, 13 | "author": "", 14 | "license": "ISC" 15 | } 16 | -------------------------------------------------------------------------------- /archive/Exam/code/review/apollo-server/index.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const { AuthenticationError } = require('apollo-server-errors'); 3 | const typeDefs = require('./typedefs.js') 4 | const { todos, users } = require('./data.js') 5 | 6 | const resolvers = { 7 | Query: { 8 | // ommitted for simplicity 9 | }, 10 | Mutation: { 11 | deleteToDo: (object, input) =>{ 12 | const decrypted = decryptedToken(input.token); 13 | todos.splice(input.index, 1); 14 | }, 15 | updateToDo: (object, input) => { 16 | const decrypted = decryptedToken(input.token); 17 | todos[input.index].title = input.title; 18 | }, 19 | loginUser: (object, params) => { 20 | const { username, password} = params; 21 | let theUser = users.find( 22 | user => user.username === username ); 23 | 24 | if(theUser === undefined){ 25 | throw new AuthenticationError( 26 | "Username undefined" 27 | ); 28 | } 29 | if(theUser.password !== password){ 30 | throw new AuthenticationError( 31 | "Wrong password" 32 | ) 33 | } 34 | return {token : jwt.sign(theUser, "12345")}; 35 | } 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /archive/Exam/code/review/apollo-server/resolvers.js: -------------------------------------------------------------------------------- 1 | const {AuthenticationError} = require("apollo-server-errors"); 2 | const jwt = require('jsonwebtoken'); 3 | const users = require('./users') 4 | 5 | const resolvers = { 6 | Query: { 7 | todos: async (parent, args, context) => { 8 | const currentUser = context.user.name; 9 | let page = 0; 10 | let size = 20; 11 | if (typeof args.page != "undefined") page = args.page; 12 | if (typeof args.size != "undefined") size = args.size; 13 | 14 | console.log('INFO - Got ALL_TODOS_QUERY from user ' + currentUser); 15 | const session = context.driver.session(); 16 | const todosQuery = await session.run( 17 | 'MATCH (t:Todo)-[:BELONGS]->(u:User)\n' + 18 | 'WHERE u.name = ' + userName + '\n' + 19 | 'RETURN t, u\n' + 20 | 'ORDER BY t.text DESC\n' + 21 | 'SKIP ' + (page * size) + '\n' 22 | 'LIMIT ' + limit + '\n'); 23 | const todos = todosQuery.records.map(todo => { 24 | let abc = todo.get('t').properties; 25 | abc.user = todo.get('u').properties; 26 | return abc; 27 | }); 28 | return todos; 29 | } 30 | }, 31 | Mutation: { 32 | loginUser: (object, params) => { 33 | const { username, password} = params; 34 | let theUser = users.find(user => user.username === username ); 35 | 36 | if(theUser === undefined){ 37 | throw new AuthenticationError( 38 | "Username undefined" 39 | ); 40 | } 41 | if(theUser.password !== password){ 42 | throw new AuthenticationError( 43 | "Wrong password" 44 | ) 45 | } 46 | return {token : jwt.sign(theUser, "12345")}; 47 | } 48 | deleteTodo: async (parent, args, context) => { 49 | todos.pop() 50 | return true 51 | } 52 | } 53 | }; 54 | 55 | module.exports = resolvers; 56 | -------------------------------------------------------------------------------- /archive/Exam/code/review/apollo-server/typedefs.js: -------------------------------------------------------------------------------- 1 | export default const typeDefs = ` 2 | type todos { 3 | title: String 4 | } 5 | type Query { 6 | todos: [todos] 7 | users: [User] 8 | } 9 | type User { 10 | name: String! 11 | password: String! 12 | id: Int 13 | } 14 | type Mutation { 15 | addToDo(title: String!, token: String!): [todos] 16 | deleteToDo(index: Int!, token: String!): [todos] 17 | updateToDo(title: String!, index: Int!, token: String!): [todos] 18 | loginUser(username: String!, password: String!): AuthPayLoad! 19 | } 20 | input UserLoginInput { 21 | username: String! 22 | password: String! 23 | } 24 | type AuthPayLoad { 25 | token: String! 26 | } 27 | ` 28 | -------------------------------------------------------------------------------- /archive/Exam/code/review/component-tests/1.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import ListItem from './ListItem' 3 | const wrapper = mount(ListItem) 4 | 5 | describe('given a `todo`', () => { 6 | it('renders todo text', () => { 7 | expect(wrapper.text()).toContain('Save'); 8 | }) 9 | 10 | it('should show an input field', () => { 11 | expect(wrapper.emitted('edit', () => { 12 | wrapper.contains('input').tobe(false) 13 | })) 14 | }) 15 | 16 | describe('click on delete button', () => { 17 | expect(wrapper.emitted('delete')) 18 | }) 19 | }) 20 | 21 | -------------------------------------------------------------------------------- /archive/Exam/code/review/component-tests/2.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import ListItem from './ListItem.vue' 3 | 4 | describe('ListItem', () => { 5 | describe('given an `item`', () => { 6 | const dummy = { id: "1", message: "Foo" }; 7 | const wrapper = mount(ListItem, { propsData: { item: dummy } }); 8 | 9 | it('init with dummy parameter', () => { 10 | expect(wrapper.vm.item).toEqual(dummy); 11 | }); 12 | 13 | it('renders item', () => { 14 | var foundItem = wrapper.find('#item-description'); 15 | var itemDescription = wrapper.vm.item.id + '. ' + wrapper.vm.item.message; 16 | expect(foundItem.text()).toEqual(itemDescription); 17 | }); 18 | 19 | describe('testing `Delete` button', () => { 20 | it('click on button emits delete event', () => { 21 | //wrapper.vm.deleteItem(); 22 | wrapper.find('#button-delete').trigger('click'); 23 | //console.log(wrapper.emitted()); 24 | var itemToDelete = wrapper.emitted('delete-item')[0][0]; 25 | //console.log(itemToDelete); 26 | expect(itemToDelete.message).toEqual(wrapper.vm.item.message); 27 | expect(itemToDelete.message).toEqual(dummy.message); 28 | }); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /archive/Exam/code/vuejs/Example.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 44 | -------------------------------------------------------------------------------- /archive/Exam/code/vuejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuejs", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "devDependencies": { 7 | "@vue/cli": "^4.1.2", 8 | "@vue/cli-service-global": "^4.1.2" 9 | }, 10 | "scripts": { 11 | "start": "vue serve Example.vue" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /archive/Exam/exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/archive/Exam/exam.pdf -------------------------------------------------------------------------------- /archive/Exam/exam.tex: -------------------------------------------------------------------------------- 1 | \documentclass[ 2 | % addpoints, ngerman 3 | addpoints,a4paper,ngerman,answers 4 | ]{exam} 5 | %\documentclass[12pt,addpoints,answers]{exam} % To print with answers 6 | \usepackage{amsmath} 7 | \usepackage{graphicx} 8 | \usepackage{setspace} 9 | \usepackage{fancybox} 10 | \usepackage{svg} 11 | \usepackage{listings} 12 | \usepackage{todonotes} 13 | \usepackage{subfig} 14 | 15 | \pointpoints{Punkt}{Punkte} 16 | \renewcommand{\solutiontitle}{\noindent\textbf{Lösung:}\enspace} 17 | 18 | 19 | \chqword{Frage} 20 | \chpgword{Seite} 21 | \chpword{Punkte} 22 | \chsword{Erreicht} 23 | \chtword{Gesamt} 24 | 25 | \usepackage[ngerman]{babel} 26 | \lstset{basicstyle=\ttfamily, 27 | showstringspaces=false, 28 | showspaces=false, 29 | showtabs=false, 30 | showstringspaces=false, 31 | commentstyle=\color{red}, 32 | keywordstyle=\color{blue} 33 | } 34 | \lstdefinelanguage{JavaScript}{ 35 | keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break}, 36 | keywordstyle=\color{blue}\bfseries, 37 | ndkeywords={class, export, boolean, throw, implements, import, this}, 38 | ndkeywordstyle=\color{darkgray}\bfseries, 39 | identifierstyle=\color{black}, 40 | sensitive=false, 41 | comment=[l]{//}, 42 | morecomment=[s]{/*}{*/}, 43 | commentstyle=\color{purple}\ttfamily, 44 | stringstyle=\color{red}\ttfamily, 45 | morestring=[b]', 46 | morestring=[b]" 47 | } 48 | 49 | 50 | 51 | 52 | 53 | \pagestyle{headandfoot} 54 | \firstpageheadrule 55 | \firstpageheader{Name:} { } {Systems-Development and Frameworks} 56 | \firstpagefooter{}{Endklausur, Seite \thepage\ von \numpages} 57 | {} 58 | \runningheader{Name:} { } {Systems-Development and Frameworks} 59 | \runningfooter{}{Endklausur, Seite \thepage\ von \numpages} 60 | {} 61 | \runningheadrule 62 | \runningfootrule 63 | 64 | %%%%% Cover page 65 | 66 | \coverfirstpageheader{ 67 | HTW Berlin \\ Angewandte Informatik 68 | }{}{ 69 | Systems-Development\\ and Frameworks 70 | } 71 | 72 | \runningheadrule 73 | \runningfootrule 74 | \onehalfspacing 75 | \newcommand{\tf}[1][{}]{% 76 | \fillin[#1][0.25in]% 77 | } 78 | 79 | \begin{document} 80 | %\maketitle 81 | \pointsinmargin % To print points in margin 82 | \boxedpoints % or \bracketedpoints 83 | 84 | \begin{coverpages} 85 | \hrule 86 | \hrule 87 | \noindent 88 | \begin{tabular}{@{}p{.4\textwidth}@{\hspace{2cm}}p{.4\textwidth}@{}} 89 | \\ 90 | Name:\enspace\hrulefill & Matrikelnummer:\enspace\hrulefill 91 | \end{tabular} 92 | \noindent 93 | \vspace{5em} 94 | 95 | \setlength{\fboxsep}{2em} 96 | \noindent\fbox{ 97 | \parbox{\textwidth - 4em}{ 98 | Deine Note in der Endklausur sagt nichts über dein Talent als 99 | Fullstack-EntwicklerIn aus. Viele wesentliche Fähigkeiten können nicht in 100 | einer schriftlichen Prüfung bewertet werden, z.B. selbstständiges Lernen, 101 | Wissensvermittlung, Einfühlungsvermögen und Hilfsbereitschaft innerhalb 102 | eines Teams.\\ 103 | 104 | Der Zweck dieser Endklausur ist es, eine Zahl für jede Teilnehmerin und 105 | jeden Teilnehmer zu ermitteln. Das ist eine formelle Anforderung der 106 | Universität.\\ 107 | 108 | Abgesehen davon dient die Klausur als Motivation für euch zum Lernen. 109 | 110 | \begin{center} 111 | Viel Erfolg! \\ 112 | Robert Schäfer 113 | \end{center} 114 | 115 | }} 116 | 117 | \vspace{3em} 118 | \begin{center} 119 | \gradetable[h][questions] 120 | 121 | % Or use \gradetable[v][questions] 122 | \end{center} 123 | 124 | \end{coverpages} 125 | 126 | \begin{questions} 127 | 128 | 129 | \question Project Management 130 | \begin{parts} 131 | \part[1] Vervollständige den folgenden Satz: 132 | Das Motto unseres Kurses ist: ``Le\_\_n and S\_\_\_e'' 133 | 134 | \part[1] 135 | Was ist der Busfaktor? Schreibe eine Definition auf. 136 | \fillwithdottedlines{4em} 137 | 138 | \part[1] 139 | Was ist besser? Ein möglichst hoher Busfaktor oder ein niedriger Busfaktor? 140 | \begin{choices} 141 | \choice Ein niedriger Busfaktor ist besser. 142 | \choice Ein hoher Busfaktor ist besser. 143 | \end{choices} 144 | 145 | \part[3] 146 | Wie kann der Wissenstransfer im Team gewährleistet werden? 147 | Nenne mindestens drei unterscheidbare Methodiken, die wir im Kurs kennen 148 | gelernt haben: 149 | \fillwithdottedlines{8em} 150 | 151 | \part[4] 152 | Wobei helfen automatisierte Software-Tests? 153 | 154 | Software-Tests eignen sich um: 155 | \begin{checkboxes} 156 | \choice Abhängigkeiten im Quellcode zu erkennen und zu vermeiden 157 | \choice das Laufzeitverhalten zu beschreiben, z.B. Komplexitätsklassen von Algorithmen 158 | \choice die Korrektheit des Quellcodes zu beweisen 159 | \choice Schnittstellen zu definieren 160 | \choice Software-Regressionen zu vermeiden 161 | \choice Projektabnahmen durch den Kunden oder Projektleiter zu ersetzen 162 | \choice das Verhalten der Anwendung zu dokumentieren 163 | \choice die Wartung des Quellcodes abzusichern, z.B. beim Refactoring 164 | \end{checkboxes} 165 | 166 | \part 167 | Wir haben in unserem Kurs einen Software-Test als ``positiv'' definiert, wenn 168 | er fehlschlägt. (Also ähnlich wie ein ``positiver'' medizinischer Test, 169 | welcher bedeutet, dass eine Testperson wahrscheinlich krank ist, z.~B. 170 | HIV-positiv.) 171 | 172 | Falsch implementierte Software-Tests können zu folgenden Problemen führen: 173 | \begin{choices} 174 | \choice Es können Sicherheitslücken entstehen. 175 | \choice Es kommt zu Verzögerungen beim Ausrollen der Anwendung (Deployment). 176 | \choice Die Endbenutzer können betroffene Funktionen der Anwendung nicht mehr nutzen. 177 | \choice Das Team verliert an Disziplin und das Vertrauen in Software-Testing. 178 | Es beginnt, Änderungen am Quellcode zu akzeptieren, obwohl der Build-Server 179 | fehlgeschlagen ist. 180 | \end{choices} 181 | 182 | \begin{subparts} 183 | \subpart[2] 184 | Welche dieser Probleme entstehen durch ``falsch-positive'' Software-Tests? 185 | 186 | Auswahl: \fillin 187 | \subpart[2] 188 | 189 | Welche dieser Probleme entstehen durch ``falsch-negative'' Software-Tests? 190 | 191 | Auswahl: \fillin 192 | \end{subparts} 193 | 194 | \part[2] 195 | Mit welcher Technik kann man ``falsch-negativen'' Tests vorbeugen? Anders 196 | gefragt: Wie kann man sicherstellen, dass ein Software-Test überhaupt einen 197 | Fehler aufzeigen würde? 198 | \fillwithdottedlines{8em} 199 | 200 | \end{parts} 201 | 202 | 203 | \question Git 204 | \begin{parts} 205 | \part[1] 206 | Ensteht bei einem \texttt{git commit --amend} eine neue commit id? 207 | \begin{choices} 208 | \choice Ja 209 | \choice Nein 210 | \end{choices} 211 | \part[1] 212 | Wie könnte man ein \texttt{git commit --amend} wieder rückgängig machen? 213 | \fillwithdottedlines{4em} 214 | 215 | \part 216 | Folgendes Szenario: Wir haben vergessen, rechtzeitig einen ``Feature-branch'' 217 | zu erstellen und jetzt zeigt der \texttt{master} branch auf den commit mit 218 | der ID~\texttt{G}. Abbildung~\ref{fig:git:initial} visualisiert den 219 | \texttt{git} Graph zu diesem Zeitpunkt. 220 | 221 | Unser Ziel ist es, nachträglich einen Feature-Branch zu erstellen, damit wir 222 | einen ``Pull Request'' mit einer gewünschten Liste an commits erstellen 223 | können. Außerdem wollen wir unseren lokalen \texttt{master} branch auf den 224 | Stand des \texttt{origin/master} zurücksetzen. Als letztes strukturieren wir 225 | die commits mit einem interaktiven \texttt{rebase} um, mit dem Ziel, unserem 226 | Team das ``Code-Review'' zu erleichtern. 227 | 228 | \begin{figure}[h] 229 | \centering 230 | \includesvg[width=0.8\textwidth]{git/initial.svg} 231 | \caption{Visualisierung des initialen \texttt{git}-Graphen.} 232 | \label{fig:git:initial} 233 | \end{figure} 234 | 235 | \newpage 236 | \begin{subparts} 237 | \subpart[1] 238 | Im ersten Schritt führen wir den folgenden Befehl aus: 239 | \begin{lstlisting} 240 | git branch feature-branch 241 | \end{lstlisting} 242 | Wie verändert sich der \texttt{git}-Graph? Skizziere den Graphen: 243 | \makeemptybox{16em} 244 | 245 | \subpart[1] 246 | Wir setzen den \texttt{master} auf den Stand des \texttt{origin/master} zurück 247 | mit diesem Befehl: 248 | \begin{lstlisting} 249 | git reset origin/master --hard 250 | \end{lstlisting} 251 | Skizziere den \texttt{git}-Graphen: 252 | \makeemptybox{16em} 253 | 254 | \subpart[1] 255 | Wir wechseln zurück auf den branch \texttt{feature-branch} mit diesem Befehl: 256 | \begin{lstlisting} 257 | git checkout feature-branch 258 | \end{lstlisting} 259 | Wie verändert sich der \texttt{git}-Graph? Skizziere den Graphen: 260 | \makeemptybox{16em} 261 | 262 | \newpage 263 | \subpart[1] 264 | Schließlich beginnen wir ein interaktives \texttt{rebase} mit diesem Befehl: 265 | \begin{lstlisting} 266 | git rebase origin/master --interactive 267 | \end{lstlisting} 268 | Danach öffnet sich der Editor mit einer Liste von commits. Welche commits 269 | befinden sich in dieser Liste? 270 | \fillwithdottedlines{3em} 271 | 272 | \subpart[1] 273 | Die Liste der commits wird neu sortiert, einige commits entfernt oder 274 | mit \texttt{squash} verschmolzen. Das sieht so aus: 275 | \begin{lstlisting} 276 | pick F commit message of F 277 | pick D squash commit message of D 278 | squash G with commit message of G 279 | \end{lstlisting} 280 | Wenn jetzt diese Änderung am ``rebase-log'' abgeschlosssen werden, dann wird 281 | der \texttt{rebase}-Prozess ausgelöst. Dieser Prozess wird aber einmal 282 | unterbrochen. Warum? 283 | \fillwithdottedlines{8em} 284 | 285 | \subpart[2] 286 | Wie sieht der \texttt{git}-Graph letztendlich aus? Wenn neue commits 287 | entstanden sind, wähle einen beliebigen, freien Buchstaben als neue Commit ID. 288 | Skizziere den finalen Graphen: 289 | \makeemptybox{16em} 290 | 291 | 292 | \end{subparts} 293 | 294 | \end{parts} 295 | 296 | 297 | 298 | \question Funktionale Programmierung 299 | 300 | \begin{parts} 301 | \part[2] Was sind ``higher order functions''? 302 | \fillwithdottedlines{6em} 303 | \newpage 304 | 305 | \part[2] Im folgenden Code-Beispiel wird eine Funktion namens 306 | \texttt{higherOrderFunction} deklariert, die Argumente der Funktion heißen 307 | \texttt{some, arguments, you, can} und \texttt{choose}. Bitte schreibe 308 | eine Implementierung, sodass die Funktion \emph{ganz offensichtlich} zu einer 309 | ``higher-order function'' wird. 310 | \begin{lstlisting}[extendedchars=true,basicstyle=\ttfamily,language=JavaScript] 311 | function higherOrderFunction(some, arguments, you, can, choose) { 312 | // Write down any implementation, which obviously turns this 313 | // function into a higher order function. 314 | // ... 315 | 316 | 317 | 318 | 319 | // return ? 320 | // If you want, return something 321 | } 322 | \end{lstlisting} 323 | 324 | \part 325 | Die folgenden Code-Beispiele enthalten unnötige temporäre Zustände. Zustände 326 | sind immer ungünstig, weil sie mögliche Fehlerquellen sein können. Wie können 327 | die folgenden Code-Beispiele umgeschrieben (refactored) werden, um die unnötigen 328 | Zustände zu eliminieren? Das Verhalten des Codes sollte sich nicht wesentlich 329 | verändern. 330 | 331 | Es gibt für jedes Code-Beispiel einen Punkt, wenn die richtige Funktion von 332 | \texttt{Array.prototype} ausgewählt wird. 333 | Für jedes korrekte Refactoring eines Code-Beispiels gibt es einen weiteren 334 | Punkt. Geringfügige Syntax-Fehler führen zu keinem Punktabzug. 335 | \begin{subparts} 336 | \subpart[2] 337 | \begin{lstlisting}[extendedchars=true,basicstyle=\ttfamily,language=JavaScript] 338 | function findTodo(todos, id){ 339 | let found 340 | for (i = 0; i < todos.length; i++) { 341 | if (todos[i].id === id) { 342 | found = todos[i] 343 | break 344 | } 345 | } 346 | return found 347 | } 348 | 349 | function refactoredFindTodo(todos, id){ 350 | // your code goes here... 351 | 352 | 353 | 354 | 355 | 356 | 357 | // return ? 358 | } 359 | \end{lstlisting} 360 | \newpage 361 | \subpart[2] 362 | \begin{lstlisting}[extendedchars=true,basicstyle=\ttfamily,language=JavaScript] 363 | function incrementIds(ids, addition = 1) { 364 | var retArray = [] 365 | for (var i = 0; i < ids.length; i++) { 366 | retArray.push(ids[i] + addition) 367 | } 368 | return retArray 369 | } 370 | 371 | 372 | function refactoredIncrementIds(ids, addition = 1) { 373 | // your code goes here 374 | 375 | 376 | 377 | 378 | 379 | // return ? 380 | } 381 | \end{lstlisting} 382 | \end{subparts} 383 | \end{parts} 384 | 385 | 386 | \question Authorization and Authentication 387 | \begin{parts} 388 | \part[2] 389 | Was bedeuten die Begriffe ``Authorization'' und ``Authentication''? Füge im 390 | folgenden Lückentext die beiden Begriffe an der richtigen Stelle ein:\\ 391 | 392 | Bei der \fillin[][2in] wird festgestellt, welcher Benutzer eine Anfrage an den 393 | Server sendet. Sobald der Benutzer bekannt ist, klärt die \fillin[][2in] ob 394 | der Benutzer auch auf eine Ressource zugreifen darf. 395 | \part[1] 396 | Werden die Nutzdaten (``Payload'') eines ``JWT Bearer token'' verschlüsselt 397 | übertragen? 398 | \begin{choices} 399 | \choice Ja 400 | \choice Nein 401 | \end{choices} 402 | \part[1] 403 | Wenn der Schlüssel zur Überprüfung der Signatur der JSON-Web-Token erneuert 404 | wird, werden daraufhin alle bis dahin ausgelieferten Token ungültig? 405 | \begin{choices} 406 | \choice Ja 407 | \choice Nein 408 | \end{choices} 409 | \end{parts} 410 | 411 | \clearpage 412 | 413 | \question GraphQL and Apollo-Server 414 | 415 | \begin{parts} 416 | \part[4] Welche Probleme löst GraphQL in Bezug auf REST? 417 | \begin{checkboxes} 418 | \choice Daten können in einer Graph-Datenbank gespeichert werden. 419 | \choice Die Anzahl der Anfragen wird minimiert. 420 | \choice HTTP-Caching Mechanismen werden besser ausgenutzt . 421 | \choice Der Server wird robuster gegenüber Denial-of-Service Angriffen. 422 | \choice Die Menge der ausgetauschen Daten pro Anfrage (der Traffic) wird minimiert. 423 | \choice Die API kann automatisch dokumentiert werden 424 | \choice Auf dem Client kann die Benutzeroberfläche automatisch aktualisiert werden. 425 | \choice Die Anzahl der Datenbank-Abfragen wird minimiert. 426 | \end{checkboxes} 427 | \part 428 | Die Methodensignatur eines resolvers enthält vier Argumente, nämlich 429 | \texttt{parent}, \texttt{args}, \texttt{context} und \texttt{resolveInfo}. 430 | \begin{subparts} 431 | \subpart[1] 432 | Sollte das Argument \texttt{context} in allen resolvern gleich sein? 433 | \begin{choices} 434 | \choice Ja 435 | \choice Nein 436 | \end{choices} 437 | \subpart[2] 438 | Wozu wird das Argument \texttt{resolveInfo} genutzt? 439 | \fillwithdottedlines{6em} 440 | \end{subparts} 441 | 442 | \part[2] 443 | Schau auf die Implementierung eines Apollo-Servers in 444 | Abbildung~\ref{fig:apollo-server} auf Seite~\pageref{fig:apollo-server}. 445 | 446 | \begin{figure}[h] 447 | \begin{lstlisting}[language=bash] 448 | query { 449 | student(id: "2") { 450 | id 451 | fullname(reverse: true) 452 | } 453 | } 454 | \end{lstlisting} 455 | \caption{GraphQL query} 456 | \label{fig:graphql-query} 457 | \end{figure} 458 | Wenn wir die GraphQL Anfrage in Abbildung~\ref{fig:graphql-query} an den 459 | Server schicken, mit welcher Antwort reagiert der Server? Schreibe das JSON 460 | Dokument auf. 461 | \makeemptybox{18em} 462 | 463 | \part Was schreibt der Server als Ausgabe aufs Terminal? Schreibe die 464 | \texttt{console.log} Ausgaben auf. Folgende korrekte Angaben werden bewertet: 465 | \begin{subparts} 466 | \subpart[1] Korrekte Auswahl, welche Log-Ausgaben überhaupt erreicht werden 467 | \subpart[1] Korrekte Anzahl aller Ausgaben 468 | \subpart[1] Korrekte Reihenfolge der Ausgaben 469 | \subpart[1] Korrekter Wert des Arguments \texttt{parent} 470 | \subpart[1] Korrekter Wert des Arguments \texttt{args} 471 | \end{subparts} 472 | \makeemptybox{18em} 473 | \begin{figure}[t!] 474 | \lstinputlisting[ 475 | basicstyle=\ttfamily, 476 | numbers=left, 477 | numberstyle=\footnotesize, 478 | numbersep=3em, 479 | extendedchars=true, 480 | language=JavaScript 481 | ]{code/graphql/index.js} 482 | \caption{Implementierung eines Apollo-Servers} 483 | \label{fig:apollo-server} 484 | \end{figure} 485 | \end{parts} 486 | 487 | 488 | \clearpage 489 | 490 | \question Neo4J 491 | \begin{parts} 492 | 493 | \part 494 | Wie wird das relationale Datenmodell in ein graph-basiertes Datenmodell 495 | übertragen? Nimm als Beispiel eine Neo4J-Datenbank. 496 | \begin{subparts} 497 | \subpart[1] Tabellen-Namen werden zu: \fillin 498 | \subpart[1] Tabellen-Spalten werden zu: \fillin 499 | \subpart[1] Einträge in Tabellen (Zeilen) werden zu: \fillin 500 | \subpart[1] Fremdschlüssel zwischen Einträgen werden zu: \fillin 501 | \end{subparts} 502 | 503 | \part 504 | Betrachte die \texttt{cypher} Anfrage: 505 | \begin{lstlisting} 506 | MATCH (node)-[*]->(yetAnotherNode) RETURN * 507 | \end{lstlisting} 508 | \begin{subparts} 509 | \subpart[2] 510 | Was bedeutet der Stern in den eckigen Klammern im ``pattern'' dieser 511 | query? 512 | \fillwithdottedlines{4em} 513 | \subpart[1] 514 | Ist so eine Anfrage auch in einer relationalen Datenbank mit SQL möglich? 515 | \begin{choices} 516 | \choice Ja 517 | \choice Nein 518 | \end{choices} 519 | \end{subparts} 520 | 521 | \part[2] Können in einer Neo4J-Datenbank Fremdschlüssel auf fehlende Einträge 522 | zeigen? Solche Zeiger ins Leere heißen auch ``dangling pointer''. Begründe 523 | deine Antwort: 524 | \fillwithdottedlines{6em} 525 | 526 | \part[1] Wieviele ``Labels'' darf ein Knoten in einer Neo4J-Datenbank haben? 527 | Antwort: \fillin 528 | 529 | \part[1] Wieviele ``Typen'' darf eine Beziehung in einer Neo4J-Datenbank haben? 530 | Antwort: \fillin 531 | 532 | 533 | \part 534 | Wir arbeiten auf einer leeren Neo4J-Datenbank. In die Neo4J-Webkonsole geben 535 | wir folgende \texttt{cypher} Befehle ein: 536 | \begin{lstlisting} 537 | CREATE(romeo:Person {name: 'Romeo'}) 538 | CREATE(juliet:Person {name: 'Juliet'}) 539 | \end{lstlisting} 540 | Abbildung~\ref{fig:neo4j:graph-merge}~(a) zeigt eine Graph-Visualisierung, 541 | nachdem wir die Befehle ausgeführt haben. 542 | \begin{figure}[h] 543 | \centering 544 | \subfloat[Vor dem \texttt{MERGE}]{\includesvg[inkscapelatex=false, width=0.2\textwidth]{./neo4j/before-merge.svg}} 545 | \qquad 546 | \qquad 547 | \subfloat[Nach dem \texttt{MERGE}]{\includesvg[inkscapelatex=false, width=0.25\textwidth]{./neo4j/after-merge.svg}} 548 | \caption{Offenbar entstehen Duplikate} 549 | \label{fig:neo4j:graph-merge} 550 | \end{figure} 551 | 552 | \newpage 553 | Nun geben wir die folgende \texttt{cypher} Befehle ein: 554 | \begin{lstlisting} 555 | MERGE(:Person {name: 'Romeo'})-[:LOVES]->(:Person {name: 'Juliet'}) 556 | \end{lstlisting} 557 | 558 | 559 | Jetzt sieht der Graph wie auf Abbildung~\ref{fig:neo4j:graph-merge}~(b) aus. 560 | \begin{subparts} 561 | \subpart[1] 562 | Wie können die \texttt{cypher} Befehle so verändert werden, dass keine 563 | Duplikate mehr entstehen? Es sollten auch nach mehrmaligen Ausführen der 564 | Befehle keine Duplikate mehr entstehen. 565 | \makeemptybox{14em} 566 | \subpart[2] 567 | Wenn wir initial auf dem Stand von Abbildung~\ref{fig:neo4j:graph-merge}~(a) 568 | \emph{zweimal hintereinander} folgende \texttt{cypher} Befehle ausführen, 569 | wie sieht der Graph dann aus? 570 | \begin{lstlisting} 571 | MATCH(romeo:Person {name: 'Romeo'}) 572 | MATCH(juliet:Person {name: 'Juliet'}) 573 | CREATE(romeo)-[:LOVES]->(juliet) 574 | \end{lstlisting} 575 | Skizziere den Graphen in der Datenbank: 576 | \makeemptybox{14em} 577 | \end{subparts} 578 | \end{parts} 579 | 580 | \clearpage 581 | 582 | \question VueJS and NuxtJS 583 | \begin{parts} 584 | \part[3] 585 | Zu den Vorteilen von client-seitig gerenderten ``Single-page-applications'' 586 | gegenüber traditionellen server-seitig gerenderten Web-Anwendungen gehören 587 | unter anderem: 588 | \begin{checkboxes} 589 | \choice Desktop-ähnliches Nutzererlebnis 590 | \choice Bessere Suchmaschinen-Optimierung 591 | \choice Kompatibilität mit älteren Browsern 592 | \choice Schneller initialer Seitenaufruf (``initial page load'') 593 | \choice Größerer Anteil Nutzdaten in der Kommunikation zwischen Client- und Server 594 | \choice Weniger Last auf dem Server 595 | \end{checkboxes} 596 | 597 | \part[3] 598 | Welche dieser sog.\ ``JavaScript globals'' sollte in Quellcode vermieden werden, 599 | welcher auch von server-seitig vorgerenderten Web-Anwendungen ausgeführt wird: 600 | \begin{checkboxes} 601 | \choice \texttt{console} 602 | \choice \texttt{document} 603 | \choice \texttt{window} 604 | \end{checkboxes} 605 | 606 | \part[1] Zu welcher Art von Fehler kommt es, wenn man trotzdem eines dieser 607 | ``globals'' in einem NodeJS Prozess aufruft? 608 | \fillwithdottedlines{4em} 609 | 610 | \part 611 | Wirf einen Blick auf die Vue-Komponente im Code-Beispiel in 612 | Abbildung~\ref{fig:vue-example}. Wenn in den folgenden Teilaufgaben Änderung 613 | im Quelltext vorgenommen werden sollen, können diese direkt in die Abbildung 614 | eingepflegt werden. 615 | \begin{subparts} 616 | \subpart[1] 617 | Offenbar hat sich ein Fehler eingeschlichen. Eigentlich soll die Methode 618 | \texttt{handleClick} beim Klick auf den Knopf ausgelöst werden. Durch welche 619 | Änderung kann der Fehler behoben werden? 620 | \subpart[1] 621 | Wenn die Methode \texttt{handleClick} aufgerufen wird, welchen Wert hat dann 622 | \texttt{message}? 623 | 624 | Antwort: \fillin 625 | \subpart[2] 626 | Füge ein \texttt{} Feld hinzu, dessen Texteingabe mit dem Zustand 627 | \texttt{message} verknüpft ist. Bei jeder Text-Eingabe soll sich also der 628 | Wert von \texttt{message} verändern. 629 | \subpart[2] 630 | Implementiere eine computed property namens \texttt{exclaimedMessage}, 631 | welches \texttt{message} zurückgibt, allerdings mit einem Ausrufezeichen am 632 | Ende. 633 | \subpart[1] 634 | Warum sind computed properties performanter als Methodenaufrufe? 635 | \fillwithdottedlines{8em} 636 | \end{subparts} 637 | \begin{figure}[h] 638 | \centering 639 | \lstinputlisting[ 640 | basicstyle=\ttfamily, 641 | numbers=left, 642 | language=HTML 643 | ]{code/vuejs/Example.vue} 644 | \caption{Eine kleine VueJS-Komponente} 645 | \label{fig:vue-example} 646 | \end{figure} 647 | \end{parts} 648 | 649 | \clearpage 650 | 651 | \question Fullstack testing and Requirements Engineering 652 | \begin{parts} 653 | \part[1] Im Kurs haben wir die ``Testing-Pyramide'' und den 654 | ``Testing-Diamanten'' kennengelernt. Beides sind Modelle, die eine 655 | Hilfestellung bieten sollen, um zu einem ausgewogenen Verhältnis der 656 | jeweiligen Test-Klassen zu finden. 657 | Welche Klasse von Tests hat laut dem ``Testing-Diamanten'' den größten Wert 658 | und sollte deshalb besonders oft implementiert werden? 659 | 660 | Antwort: \fillin 661 | 662 | \part[2] Was ist das Alleinstellungsmerkmal von Cucumber Tests? 663 | \begin{choices} 664 | \choice ``Specification by example'', d.h. anstelle abstrakter Dokumentation 665 | wird das Verhalten der Anwendung anhand von Beispielen beschrieben. 666 | \choice ``Executable documentation'', d.h. dadurch dass die Dokumentation 667 | regelmäßig vom Build-Server ausgeführt wird, kann die Dokumentation nicht 668 | mehr veralten (``out-of-date''). 669 | \choice Software-Tests werden in menschliche Sprache übersetzt, sodass auch 670 | Nicht-Programmierer die Tests lesen und nachvollziehen können. 671 | \end{choices} 672 | 673 | \part[2] Die Schablone eine User-Story sieht wie folgt aus: 674 | \begin{lstlisting} 675 | As a 676 | I want to do 677 | In order to 678 | \end{lstlisting} 679 | Diese Schablone soll dazu dienen, dass man bei der Erfassung von Anforderungen 680 | bestimmte Informationen nicht vergisst, z.B. die Zielgruppe und den Grund für 681 | eine gewünschte Funktion. Warum ist es so wichtig, diese Informationen stets 682 | zu dokumentieren? 683 | \fillwithdottedlines{8em} 684 | \end{parts} 685 | 686 | \newpage 687 | \question CSS 688 | \begin{figure}[h] 689 | \centering 690 | \lstinputlisting[ 691 | numbers=left, 692 | language=HTML 693 | ]{code/css/example.html} 694 | \caption{Ein beispielhaftes HTML Dokument} 695 | \label{fig:example-html} 696 | \end{figure} 697 | \begin{parts} 698 | \part[3] 699 | Welche HTML-Elemente werden bzw.\ welches HTML-Element wird in Abbildung~\ref{fig:example-html} 700 | mit den folgenden CSS Selektoren selektiert? Gib als Antwort Zeilennummern an: 701 | \begin{subparts} 702 | \subpart 703 | \begin{lstlisting} 704 | .reaction-buttons 705 | \end{lstlisting} 706 | 707 | Antwort: \fillin 708 | 709 | \subpart 710 | 711 | \begin{lstlisting} 712 | .post-footer > .button 713 | \end{lstlisting} 714 | 715 | Antwort: \fillin 716 | 717 | \subpart 718 | 719 | \begin{lstlisting} 720 | .post-footer :first-child 721 | \end{lstlisting} 722 | 723 | Antwort: \fillin 724 | \end{subparts} 725 | 726 | \part[1] 727 | Mit welchen CSS-Selektor können alle Buttons selektiert werden? 728 | 729 | Antwort: \fillin[][2in] 730 | 731 | \end{parts} 732 | 733 | 734 | \question Code Review 735 | \begin{parts} 736 | \part[10] Die folgenden drei Code-Beispiele sind Auszüge aus den Abgaben der 737 | Hausaufgaben und enthalten Schwächen. Finde diese Schwächen und schlage eine 738 | Verbesserung vor. Wenn du eine Schwäche finden kannst und eine sinnvolle 739 | Verbesserung vorschlägst, gibt es dafür einen Punkt.\\ 740 | 741 | Über einige dieser Schwächen haben wir ausführlich während der Vorlesung und 742 | während der Übungen und in den Code-Reviews gesprochen. Dazu gehören: 743 | \begin{itemize} 744 | \item 745 | Häufige Ursachen für ``falsch-negative'' oder ``falsch-positive'' Tests, 746 | \item 747 | Sicherheitslücken oder unnötige Preisgabe von empfindlichen Informationen, 748 | \item 749 | häufige Ursachen für Programmfehler, z.~B. vermeidbare Zustände, 750 | \item 751 | Verletzungen von Spezifikationen, 752 | \item 753 | Anti-Patterns in Bezug auf Frameworks. 754 | \end{itemize} 755 | Es gibt einen weiteren Punkt, wenn du erklärst, wie man diese 756 | ``gröberen'' Fehler in Zukunft verhindern kann. Auf der letzten Seite 757 | gibt es genug Platz für Erklärungen. 758 | 759 | 760 | Es spielt keine Rolle, welches Code-Beispiel du dir aussuchst, es werden alle 761 | Verbesserungen und Erklärungen für diese Aufgabe summiert. 762 | \begin{figure}[h] 763 | \lstinputlisting[ 764 | basicstyle=\ttfamily, 765 | numbers=left, 766 | numberstyle=\footnotesize, 767 | numbersep=3em, 768 | extendedchars=true, 769 | language=JavaScript 770 | ]{code/review/component-tests/2.js} 771 | \caption{Auszug aus einem VueJS Komponenten Test} 772 | \end{figure} 773 | \begin{figure}[h] 774 | \lstinputlisting[ 775 | basicstyle=\ttfamily, 776 | numbers=left, 777 | numberstyle=\footnotesize, 778 | numbersep=3em, 779 | extendedchars=true, 780 | language=JavaScript 781 | ]{code/review/apollo-server/typeDefs.js} 782 | \caption{Die GraphQL Typendefinitionen eines Apollo-Servers. Mit \texttt{token} 783 | ist ein JWT Bearer token gemeint. Das Feld \texttt{password} ist tatsächlich 784 | ein Benutzerpasswort und ist entsprechend sensibel.} 785 | \end{figure} 786 | \begin{figure}[h] 787 | \lstinputlisting[ 788 | basicstyle=\ttfamily, 789 | numbers=left, 790 | numberstyle=\footnotesize, 791 | numbersep=3em, 792 | extendedchars=true, 793 | language=JavaScript 794 | ]{code/review/apollo-server/resolvers.js} 795 | \caption{Auszüge aus GraphQL resolver Implementierungen} 796 | \end{figure} 797 | 798 | \clearpage 799 | Hier können Probleme ausführlicher erklärt werden, wenn der Platz neben dem 800 | Code nicht ausreichen sollte. Bitte die Zeilennummer und die Abbildung 801 | angeben, um die Zuordnung zu erleichtern. 802 | \fillwithdottedlines{\stretch{1}} 803 | 804 | \end{parts} 805 | 806 | \end{questions} 807 | \end{document} 808 | 809 | %%% Local Variables: 810 | %%% mode: latex 811 | %%% TeX-master: t 812 | %%% End: 813 | -------------------------------------------------------------------------------- /archive/Exam/git/initial.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /archive/Exam/neo4j/after-merge.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | Neo4j Graph Visualization 26 | 27 | 28 | 29 | 31 | 55 | Neo4j Graph Visualization 57 | Created using Neo4j (http://www.neo4j.com/) 59 | 63 | 67 | 73 | LOVES 80 | 81 | 82 | 86 | 90 | 97 | 103 | Romeo 109 | 110 | 114 | 121 | 127 | Juliet 133 | 134 | 138 | 145 | 151 | Romeo 157 | 158 | 162 | 169 | 175 | Juliet 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /archive/Exam/neo4j/before-merge.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | Neo4j Graph Visualization 26 | 27 | 28 | 29 | 31 | 55 | Neo4j Graph Visualization 57 | Created using Neo4j (http://www.neo4j.com/) 59 | 63 | 67 | 71 | 78 | 84 | Romeo 90 | 91 | 95 | 102 | 108 | Juliet 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /archive/Exam/project-management/software-testing-benefits.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /exercises/1/README.md: -------------------------------------------------------------------------------- 1 | # VueJS 2 | 3 | 4 | Due date: **28.10.2020** 5 | 6 | 7 | ## Goal 8 | 9 | The goal is to reimplement some basic functionalities of [Hackernews](https://news.ycombinator.com/) in VueJS to learn the concepts. 10 | 11 | Hacker News is a social news website focusing on computer science and entrepreneurship. Content can be submitted by users and can be interacted via upvotes or comments. We will focus on the startpage: 12 | 13 |

14 | hackernews 15 |

16 | 17 | ### Demo 18 | 19 | This is how the result should look like: 20 | 21 | 22 |

23 | Demo 24 |

25 | 26 | You can use [Vue CLI](https://cli.vuejs.org/) for a quick setup which will setup [single file components](https://vuejs.org/v2/guide/single-file-components.html) for you. 27 | 28 | Note that the official documentation of VueJS does not recommend using `vue-cli` but instead suggests to use a plain [index.html](https://github.com/vuejs/vuejs.org/blob/master/src/v2/examples/vue-20-hello-world/index.html) file. 29 | 30 | Either way is fine, using `vue-cli` is easier but setting up VueJS yourself might be more educational. 31 | 32 | Put your code somewhere in this repository. We suggest a monorepo approch with a folder `webapp` below the repository's root folder. 33 | 34 | ### Objectives 35 | 36 | 37 | Each star gives one point if implemented satisfactorily. 38 | 39 | :star: First of all, push new commits from `main` branch of the [homework repository](https://github.com/Systems-Development-and-Frameworks/homework/) to your team repository. 40 | 41 | Implement a prototype using VueJS which contains: 42 | 43 | :star: A list that renders a number of news items 44 | - The initial list can be hard-coded 45 | - Each item has a `title` and `votes`, the latter are 0 initially. 46 | 47 | :star: Each item has a button to remove itself 48 | - The item should not remove itself from the DOM but tell its parent component to do so. 49 | 50 | :star: Each item has buttons to downvote or upvote itself, respectively 51 | - Each item `$emit('update', item)` the updated item 52 | - The parent component updates the list when an item is updated 53 | 54 | :star: Below the list is a form to add new items to the list 55 | - Prevent the default behaviour of a `

` html element which would execute a HTTP request on submit. 56 | 57 | Make sure that your prototype has these properties: 58 | 59 | :star: The items are always ordered by votes in descending order 60 | - You could use computed properties for the list of ordered items 61 | - `Array.prototype.sort` has side-effects. You can create a new array with `[...array]` destructuring. 62 | 63 | :star: There are no runtime errors 64 | - E.g. no duplicate keys 65 | 66 | Furthermore: 67 | 68 | :star: Components have clean responsibilities 69 | - Data down, actions up 70 | 71 | :star: Use functional programming paradigms, e.g.: 72 | - [Array.prototype.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) 73 | - [Array.prototype.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) 74 | - [Array.prototype.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) 75 | 76 | :star: Your PR does not contain unrelated changes 77 | - Double check "Files changed" tab in your pull request 78 | -------------------------------------------------------------------------------- /exercises/1/images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/exercises/1/images/demo.gif -------------------------------------------------------------------------------- /exercises/1/images/hackernews.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/exercises/1/images/hackernews.png -------------------------------------------------------------------------------- /exercises/2/README.md: -------------------------------------------------------------------------------- 1 | # Exercise #2 2 | 3 | | Deadline | Date | 4 | | -------------------------- | ---------------------- | 5 | | Review due date (optional) | 04.11.2020 - 14:00 | 6 | | **Final Due date** | **11.11.2020 - 14:00** | 7 | 8 | ## Goal 9 | 10 | You will learn test-driven-development (TDD) and how to setup your own build server for continuous integration and continuous deployment (CI/CD). Also you will learn how to do component-driven-development (CDD) using [Storybook](https://www.learnstorybook.com/). 11 | 12 | Extend exercise #1 with the following objectives. 13 | 14 | ### Objectives 15 | :star: Add a linter to your project, e.g. `eslint` and/or `prettier`. 16 | - `yarn run lint` should fail in case of lint errors 17 | 18 | :star: Test your components with [jest](https://jestjs.io/) and [vue-test-utils](https://vue-test-utils.vuejs.org/). 19 | - `yarn run test:unit` runs all tests 20 | 21 | Test-drive the following features: 22 | - :star: When all items are deleted from the list, it shows sth. like "The list is empty :(" 23 | - :star: A button on the news list toggles between descending and ascending order 24 | 25 | A sample output could look like this: 26 | ``` 27 | $ yarn run test --verbose 28 | PASS src/components/NewsList/NewsList.spec.js 29 | NewsList.vue 30 | :initialItems 31 | empty 32 | ✓ renders empty state (14ms) 33 | not empty 34 | ✓ renders for each item (5ms) 35 | click "Reverse order" 36 | ✓ toggles between ascending and descending order (14ms) 37 | ``` 38 | 39 | :star: [Configure jest](https://jestjs.io/docs/en/configuration) so that you can co-locate test and component files: 40 | ``` 41 | YourComponent/ 42 | YourComponent.vue 43 | YourComponent.spec.js 44 | ``` 45 | 46 | :star: Setup a build server for continuous integration, use either [Github Actions](https://github.com/features/actions), [Travis CI](https://travis-ci.org/) or [Circle Ci](https://circleci.com/) (Github Actions is probably the easiest, but feel free to use what you're comfortable with) 47 | - Your build server must: 48 | - check lint 49 | - check your tests 50 | - update [status checks](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/about-status-checks) on all PRs to your repository 51 | 52 | :star: Create a [storybook](https://storybook.js.org/docs/vue/get-started/install) for each of your VueJS components 53 | - Co-locate storybook with component files: 54 | ``` 55 | YourComponent/ 56 | YourComponent.vue 57 | YourComponent.spec.js 58 | YourComponent.stories.js 59 | ``` 60 | - Add storybook addons `@storybook/addon-essentials` along with `@storybook/addon-a11y` 61 | - Hint: For setup you can run `npx sb init` 62 | 63 | :star: Configure `@storybook/addon-controls` so that you can interactively update your components properties in real-time. 64 | - Ensure `@storybook/addon-knobs` is not installed 65 | 66 | :star: Make sure your components pass all accessiblity tests, including the component rendering the `` 67 | 68 | 69 | :star: Review a pull request of another team. 70 | - Either "Request Changes" or "Approve" *do not just "Comment"*. 71 | - Suggest changes to the code in "Files Changed" (you should learn how to do that). 72 | - Request a review from another team as well 73 | 74 | ### Screenshots 75 | 76 | ![Storybook controls example](./images/storybook-controls-example.png) 77 | 78 | ![Storybook a11y example](./images/storybook-a11y-example.png) 79 | 80 | 81 | ### Optional objectives 82 | 83 | In our lecture of October 21st 2020 we covered online open-source communities and git internals. These topics are not (yet) part of the current homework but will be a task in the upcoming homeworks and/or the final exam. So it's a great opportunity for you to practice the following: 84 | 85 | :rocket: Record an online pair-programming. 86 | - Choose someone who is not a member of your team, preferably some member of an open-source online community but a member of another team is fine, too. 87 | - Record the pair-programming to [PeerTube](https://joinpeertube.org/en/) or [YouTube](https://www.youtube.com/) and send the link to the video to us 88 | - Kudos if you publish the video :+1: otherwise mark the video as "unlisted" 89 | 90 | :rocket: Ensure meaningful commit messages and `git commit --amend` your commits. (This is why we learned how git compares commits and which action will change the commit id) 91 | -------------------------------------------------------------------------------- /exercises/2/images/storybook-a11y-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/exercises/2/images/storybook-a11y-example.png -------------------------------------------------------------------------------- /exercises/2/images/storybook-controls-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Systems-Development-and-Frameworks/homework/f0ee9706ac724c9992e255d5bd8ab62b59096525/exercises/2/images/storybook-controls-example.png -------------------------------------------------------------------------------- /exercises/3/README.md: -------------------------------------------------------------------------------- 1 | # Exercise #3 2 | 3 | | Deadline | Date | 4 | | -------------------------- | ---------------------- | 5 | | Review due date (optional) | 18.11.2020 - 14:00 | 6 | | **Final Due date** | **25.11.2020 - 14:00** | 7 | 8 | ## Goal 9 | 10 | You will learn the concepts of [GraphQL](https://graphql.org/), how to setup your own resolvers with apollo-server and how to do test-driven development (TDD) with [apollo-server-testing](https://www.apollographql.com/docs/apollo-server/testing/testing/). 11 | 12 | Because `News` is [uncountable in English](https://www.espressoenglish.net/is-news-singular-or-plural/#:~:text=The%20word%20%E2%80%9Cnews%E2%80%9D%20in%20English,never%20use%20a%20or%20an.) we're going 13 | to use the term `Post` for the entity which has a `title` and `votes`. 14 | 15 | Extend exercise [#1](../1) and [#2](../2) with the new [objectives](#objectives). 16 | 17 | ## Instructions 18 | 19 | 1. Add a `backend` folder in your root directory. If necessary, restructure your project so `webapp` is also in your root directory. Make sure moved files/folders show as 'renamed' rather than removed and created to contain the files history. In the end, you want a structure similar to this: 20 | 21 | ```console 22 | $ ls 23 | archive/ backend/ exercises/ webapp/ README.md 24 | ``` 25 | 26 | 2. [Setup your graphql backend with apollo-server](https://www.apollographql.com/docs/apollo-server/getting-started/) in your `backend` folder. Setup a linter. You can use `npx eslint --init` to setup eslint. 27 | 28 | 3. Setup [apollo-server-testing](https://www.apollographql.com/docs/apollo-server/testing/testing/) and add it to your CI/CD pipeline. Setting up `jest` is described [here](https://jestjs.io/docs/en/getting-started#generate-a-basic-configuration-file). 29 | 30 | 4. Implement a custom `apollo data source` (General information [here](https://www.apollographql.com/docs/apollo-server/data/data-sources/) and [here](https://www.apollographql.com/blog/a-deep-dive-on-apollo-data-sources/)). Data can be stored in-memory, no persisting required. For convenience, you can (optionally) seed your database with dummy data when your server starts. Here's a blueprint: 31 | ```js 32 | import { DataSource } from 'apollo-datasource' 33 | 34 | export default class InMemoryDataSource extends DataSource { 35 | constructor () { 36 | super() 37 | } 38 | 39 | initialize ({ context }) {} 40 | 41 | allPosts () {} 42 | createPost (data) {} 43 | upvotePost(id, user) {} 44 | } 45 | ``` 46 | 47 | 5. Implement resolvers for the [target schema](#target-schema) and test-drive the following features: 48 | 1. List all posts 49 | 2. List all users 50 | 3. Create a post 51 | 4. Upvote a post 52 | 53 | Features 3 and 4 need information who is issuing the request. To keep this exercise small, authentication/authorization will be covered in *next exercise*. For now, you can mock the `currentUser` by passing it in `args` (according to schema). 54 | 55 | For Feature 4, make sure that every user can upvote a post *only once*. Trying to upvote a post multiple times has no effect. 56 | 57 | Also, make sure that it's possible to nest your queries 'indefinitely' without nullifying a nested resource. This should work: 58 | ```gql 59 | { 60 | posts { 61 | title 62 | author { 63 | name 64 | posts { 65 | title 66 | author { 67 | name 68 | # and so on 69 | } 70 | } 71 | } 72 | } 73 | } 74 | ``` 75 | 76 | 6. PR Review: 77 | 1. Review a pull request of another team. 78 | * Find at least 6 things (:star: from [Objectives](#objectives)) the other team did or didn't do. 79 | * Either "Request Changes" or "Approve" *do not just "Comment"*. 80 | * Suggest changes line-by-line in "Files Changed". 81 | * Link to your code review in the description of your own pull request 82 | 2. Request a review from another team 83 | 84 | ## Objectives 85 | 86 | :star: For correct folder structure, basic apollo server setup and it's linter in the CI/CD pipeline. 87 | 88 | :star: For the `apollo-server-testing` setup and its integration in the CI/CD pipeline. 89 | 90 | :star: For implementing a custom data source for apollo-server 91 | 92 | For implementing resolvers for the [target schema](#target-schema) and the following features: 93 | 1. :star: For listing all posts 94 | 2. :star: For listing all users 95 | 3. :star: For creating a post 96 | 4. :star: For upvoting a post 97 | 98 | :star: For preventing users to upvote the same post multiple times. 99 | 100 | :star: For 'indefinitely' nestable queries. 101 | 102 | :star: For requesting review and reviewing another teams PR according to the instructions. 103 | 104 | 105 | ## Hints 106 | 107 | * JavaScript has [Sets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set), and they are a good option to count the upvotes in this task. 108 | * For easier debugging you could disable automatic polling in the [graphql playground settings](https://www.apollographql.com/docs/apollo-server/testing/graphql-playground/#configuring-playground), set `schema.polling.enable` to `false`. 109 | * If nodejs is not terminating with `Ctrl+C`, follow [this Stackoverflow answer](https://stackoverflow.com/a/53535316) to gracefully shutdown with `SIGINT`. 110 | * If you want universal-hot-reload, see [here](https://github.com/yusinto/universal-hot-reload). 111 | * Random ids can be generated with node's built-in [crypto library](https://stackoverflow.com/a/40191779). 112 | * ES6 `import/export` statements are optional for this task, but they are considered best practice. If you want to show off, you can for instance use [esm](https://github.com/standard-things/esm) to make use of it. Just run `yarn add --dev esm` and add `"dev": "node -r esm index.js",` to your `package.json`. 113 | 114 | ### Example Test Case (Mutation) 115 | 116 | This example 117 | 118 | * uses [graphql variables](https://graphql.org/learn/queries/#variables), 119 | * shows how to use [.resolves/.rejects](https://jestjs.io/docs/en/asynchronous#resolves--rejects) 120 | * and how to check that `errors` is `undefined`, 121 | * as well as jest's [toMatchObject](https://jestjs.io/docs/en/expect#tomatchobjectobject) 122 | * in combination with [expect.any](https://jestjs.io/docs/en/expect#expectanyconstructor). 123 | 124 | ```js 125 | describe('write(post: $postInput)', () => { 126 | const opts = { 127 | mutation: gql` 128 | mutation($postInput: PostInput!) { 129 | # ... 130 | } 131 | `, 132 | variables: { /* ... */ } 133 | } 134 | 135 | it('creates and returns a post', async () => { 136 | await expect(mutate(opts)).resolves.toMatchObject({ 137 | errors: undefined, 138 | data: { 139 | write: { 140 | id: expect.any(String), 141 | title: 'New post', 142 | author: { name: 'Alice' } 143 | } 144 | } 145 | }) 146 | }) 147 | }) 148 | ``` 149 | 150 | 151 | ### Target Schema 152 | 153 | ```gql 154 | type Post { 155 | id: ID! 156 | title: String! 157 | votes: Int! 158 | author: User! 159 | } 160 | 161 | type User { 162 | name: ID! 163 | posts: [Post] 164 | } 165 | 166 | type Query { 167 | posts: [Post] 168 | users: [User] 169 | } 170 | 171 | type Mutation { 172 | write(post: PostInput!): Post 173 | # 🚀 OPTIONAL 174 | # delete(id: ID!): Post 175 | 176 | # ⚠️ FIXME in exercise #4 177 | # mock voter until we have authentication 178 | upvote(id: ID!, voter: UserInput!): Post 179 | 180 | # 🚀 OPTIONAL 181 | # downvote(id: ID!, voter: UserInput!): Post 182 | } 183 | 184 | input PostInput { 185 | title: String! 186 | 187 | # ⚠️ FIXME in exercise #4 188 | # mock author until we have authentication 189 | author: UserInput! 190 | } 191 | 192 | input UserInput { 193 | name: String! 194 | } 195 | 196 | ``` 197 | 198 | ## Optional objectives 199 | 200 | :rocket: Implement and test resolvers for the mutations marked as `OPTIONAL` 201 | 202 | :rocket: Setup and configure [apollo-client](https://apollo.vuejs.org/guide/installation.html#vue-cli-plugin) in your webapp 203 | 204 | :rocket: Call the GraphQL endpoint from your webapp 205 | -------------------------------------------------------------------------------- /exercises/4/README.md: -------------------------------------------------------------------------------- 1 | # Exercise #4 2 | 3 | | Deadline | Date | 4 | | -------------------------- | ---------------------- | 5 | | Review due date (optional) | 02.12.2020 - 14:00 | 6 | | **Final Due date** | **09.12.2020 - 14:00** | 7 | 8 | ## Goal 9 | 10 | In this exercise, you're going to learn the concepts of [Authentication](https://en.wikipedia.org/wiki/Authentication) with [JWT](https://jwt.io/) and [Authorization](https://en.wikipedia.org/wiki/Authorization) with [graphql-shield](https://github.com/maticzav/graphql-shield). For the latter, you need an understanding of the "middleware" software pattern as defined by [express](https://github.com/maticzav/graphql-shield). An implementation for [apollo-server](https://github.com/apollographql/apollo-server) is [graphql-middleware](https://github.com/prisma-labs/graphql-middleware). 11 | 12 | Extend exercise [#1](../1), [#2](../2) and [#3](../3) with the new [objectives](#objectives). 13 | 14 | ## Instructions 15 | 16 | 1. Refactor your current graphql schema to match the new [target schema](#target-schema). 17 | 2. Implement a signup mutation for new users using email and password 18 | * Accept only passwords with a length of at least 8 characters. 19 | * Make sure the `email` address is not taken by another user. 20 | 3. Implement a login mutation for existing (signed up) users. 21 | * Add a graphql [context](https://www.apollographql.com/docs/apollo-server/api/apollo-server/#context) to your Apollo Server and verify a [JWT Bearer token](https://jwt.io/introduction/) from the `Authorization` HTTP request header, see our [hints](#hints). 22 | 4. Do not commit common security mistakes: 23 | * Use [bcrypt](https://www.npmjs.com/package/bcrypt) to hash passwords. 24 | * Do not check your JWT secret into version control, see our [hints](#hints). 25 | * Do not expose password hashes in the JWT payload. Also do not expose private information like `email` unnecessarily. 26 | * Specific to our use-case: Make sure a JWT is only valid if a corresponding user in the database exists. The user might have been deleted or disabled since the JWT was issued. 27 | 5. Test-drive the following refactorings: 28 | * `write` a post assigns the authenticated user as author. 29 | * Unauthenticated requests throw an error. 30 | * `upvote` a post assigns the authenticated user as voter. 31 | * Unauthenticated requests throw an error. 32 | 6. PR Review: 33 | * Review a pull request of another team. 34 | * Find at least 6 things (:star: from [Objectives](#objectives)) the other team did or didn't do. 35 | * Either "Request Changes" or "Approve" *do not just "Comment"*. 36 | * Suggest changes line-by-line in "Files Changed". 37 | * Link to your code review in the description of your own pull request. 38 | * Request a review from another team 39 | 7. Rebase your `main` branch on top of `homework/main`. 40 | 41 | ## Objectives 42 | 43 | :star: For an implemented `signup` feature. 44 | 45 | :star: For a software-tested `signup` feature. 46 | 47 | :star: For password validation. 48 | 49 | :star: For unique email address validation. 50 | 51 | :star: For not having security issues according to instructions. 52 | 53 | :star: For an implemented `login` feature using JWT. 54 | 55 | :star: For a software-tested `login` feature. 56 | 57 | :star: For assigning the authenticated user to a post in `write` and `upvote` mutations. 58 | 59 | :star: For requesting a review and reviewing another team's PR according to the instructions. 60 | 61 | :star: For a successful rebase on `homework/main`. 62 | 63 | All objectives must be implemented according to the [instructions](#instructions). 64 | 65 | ## Optional Objectives 66 | 67 | :rocket: For software-tested authorization errors on `write` and `upvote` mutations. 68 | 69 | :rocket: For authorizing read access on `User.email`: 70 | * Allows access only if `email` belongs to authenticated user. 71 | 72 | :rocket: For authorizing `delete` mutation: 73 | * Only authors of a post are allowed to delete their own posts. 74 | 75 | :rocket: For adding a fallback authorization rule: 76 | * Access is denied on all mutations and queries which are not explicitly white-listed. 77 | 78 | ## Hints 79 | 80 | * [nodemon](https://www.npmjs.com/package/nodemon) is an alternative to [unversal-hot-reload](https://www.npmjs.com/package/universal-hot-reload) in development. 81 | * If you need, you could use mock function [matchers](https://jestjs.io/docs/en/mock-functions#custom-matchers) to assert certain functions of your custom `DataSource` have been called. 82 | * Use [resolvers merging](https://www.graphql-tools.com/docs/merge-resolvers/) to split your resolvers into multiple files. 83 | * Use JavaScript [Classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) to ensure data integrity, encrypt passwords and set default values. 84 | * Here is a blueprint for your [context function](https://www.albertgao.xyz/2019/12/01/how-to-use-graphql-shield-with-apollo-server-to-authorize-JWT/). 85 | * For authorization, install [graphql-middleware](https://github.com/prisma-labs/graphql-middleware) and [graphql-shield](https://github.com/maticzav/graphql-shield) and make yourself familiar with both. 86 | * Be aware that we don't use `graphql-yoga`, so follow the instructions for the [standalone usage](https://github.com/prisma-labs/graphql-middleware#standalone-usage) instead. 87 | * Use the [dotenv](https://www.npmjs.com/package/dotenv) package to load secrets from your environment variables or a local `.env` file. 88 | 89 | ### Target Schema 90 | 91 | ```gql 92 | type Post { 93 | id: ID! 94 | title: String! 95 | votes: Int! 96 | author: User! 97 | } 98 | 99 | type User { 100 | # ⚠️ attributes 'id' and 'name' have changed! 101 | # 'id' now represents a randomly generated string, similar to 'Post.id' 102 | id: ID! 103 | name: String! 104 | email: String! 105 | posts: [Post] 106 | } 107 | 108 | type Query { 109 | posts: [Post] 110 | users: [User] 111 | } 112 | 113 | type Mutation { 114 | write(post: PostInput!): Post 115 | upvote(id: ID!): Post 116 | # 🚀 OPTIONAL 117 | # downvote(id: ID!): Post 118 | # 🚀 OPTIONAL 119 | # delete(id: ID!): Post 120 | 121 | """ 122 | returns a signed JWT or null 123 | """ 124 | login(email: String!, password: String!): String 125 | 126 | """ 127 | returns a signed JWT or null 128 | """ 129 | signup(name: String!, email: String!, password: String!): String 130 | } 131 | 132 | input PostInput { 133 | title: String! 134 | } 135 | ``` 136 | -------------------------------------------------------------------------------- /exercises/5/README.md: -------------------------------------------------------------------------------- 1 | # Exercise #5 2 | 3 | | Deadline | Date | 4 | | -------------------------- | ---------------------- | 5 | | Review due date (optional) | 16.12.2020 - 14:00 | 6 | | **Final Due date** | **10.01.2021 - 14:00** | 7 | 8 | ## Goal 9 | 10 | We, the [`@mentors`](https://github.com/orgs/Systems-Development-and-Frameworks/teams/mentors), decided to focus in this exercise on an advanced feature of GraphQL: [GraphQL 11 | schema stitching](https://www.graphql-tools.com/docs/stitch-combining-schemas) 12 | and [schema delegation](https://www.graphql-tools.com/docs/schema-delegation/#delegatetoschema). 13 | 14 | Extend exercise [#1](../1), [#2](../2), [#3](../3) and [#4](../4) with the new 15 | [objectives](#objectives). 16 | 17 | You can choose between two different architectures: 18 | 1. Remote GraphQL API 19 | 2. Local Neo4J DB 20 | 21 | ## Instructions 22 | 23 | 1. Choose a GraphQL [architecture](#architectures) fitting to your needs. Write 24 | installation instructions in the `README.md` and explain why you have chosen the 25 | architecture based on your use-case. Be creative. 26 | 27 | 2. If you require API keys (e.g. [Remote API](#using-a-remote-graphqlrest-api) 28 | scenario), put them into a `.env` file and encrypt this file with [git-crypt](https://github.com/AGWA/git-crypt). 29 | Do the following: 30 | * Install `git-crypt`, 31 | * rebase your branch on latest `homework/main`, 32 | * download the symmetric key from our [Moodle course](https://moodle.htw-berlin.de/course/view.php?id=29222), 33 | * move the symmetric key to `/git-crypt-key`, 34 | * run `git-crypt unlock git-crypt-key` in the root folder, 35 | * and add your files to `/.gitattributes` according to the [documentation](https://github.com/AGWA/git-crypt#using-git-crypt). 36 | 37 | 3. Refactor your mutations and queries. Ensure that data created during a 38 | mutation is persisted, ie. it is still there after a server restart. Any 39 | mutation which was required in a previous exercise and does not persist data 40 | will cost you a :star:. Queries and mutations must get their data from your 41 | chosen database or API endpoint. 42 | 43 | 4. Ensure that your software tests have no side-effects, ie. your tests do not 44 | interfere with each other. Any software test case that introduces a side-effect 45 | will cost you a :star:. Do not simply delete software tests that were required 46 | previously. You can ensure side-effect-free tests by either: 47 | * Cleaning the database before or after your tests 48 | * or not writing any data in your tests. E.g. you mock the response of your 49 | database or remote API. 50 | 51 | 5. Make sure your database is never left in some invalid state after a mutation. 52 | An example is an orphaned post, ie. a post without author. In case you have 53 | multiple database calls in one of your resolvers, run them in a transaction. 54 | 55 | 6. Your target schema remains the same and must behave as expected. Additional 56 | mutations and queries that are introduced by a subschema must throw a 57 | `Forbidden` GraphQL error when called by the client directly. 58 | 59 | 7. PR Review: 60 | * Review a pull request of another team. 61 | * Find at least 6 things (:star: from [Objectives](#objectives)) the other 62 | team did or didn't do. 63 | * Either "Request Changes" or "Approve" *do not just "Comment"*. 64 | * Suggest changes line-by-line in "Files Changed". 65 | * Link to your code review in the description of your own pull request. 66 | * Request a review from another team 67 | 68 | ## Objectives 69 | 70 | :star: For choosing a scenario and writing installation instructions in `README.md`. 71 | 72 | :star: For explaining *WHY* you have chosen the scenario in the `README.md`. 73 | 74 | :star: For not committing sensitive secrets (e.g. API keys) unencrypted to the 75 | repository. 76 | 77 | :star: :star: Data created in mutations are persisted and your queries 78 | and mutations still work as expected. 79 | 80 | :star: :star: Your software tests are free of side effects. 81 | 82 | :star: Your database is never left in an invalid state. 83 | 84 | :star: You deny the client to call mutations and queries of your subschema 85 | directly. 86 | 87 | :star: For requesting a review and reviewing another team's PR. 88 | 89 | All objectives must be implemented according to the [instructions](#instructions). 90 | 91 | ## Architectures 92 | 93 | Choose one of the architectures below. Combinations are possible. 94 | 95 | ### Using a Remote GraphQL API 96 | 97 | *This is probably the easier solution!* 98 | 99 | If you don't want to host a database yourself, you can use a headless CMS which 100 | comes with a nice UI for your administrators and editors. 101 | 102 | ![Remote GraphQL API](../../.github/img/scenario2.png) 103 | 104 | Please, encrypt your API keys with a symmetric key shared in our [Moodle course](https://moodle.htw-berlin.de/course/view.php?id=29222) and commit them to your repository. This is an [objective](#objectives). 105 | 106 | Since you are using an external API, you **must** use mocks in your software 107 | tests when your resolvers would otherwise call your endpoint. You do not want 108 | your build server to produce traffic during builds or other side-effects. 109 | You can generate the mock gql file with [get-graphql-schema from Prisma](https://github.com/prisma-labs/get-graphql-schema) 110 | 111 | You must choose a headless CMS with a GraphQL endpoint that supports queries 112 | **and mutations**. Have a look into [this comparison](https://cms-comparison.io/#/list) 113 | and order by "GraphQL: Mutations". 114 | 115 | A suggestion is e.g. [GraphCMS](https://graphcms.com/). 116 | 117 | Here is [demo code](https://github.com/Systems-Development-and-Frameworks/homework/tree/demo/demo) 118 | how to setup schema delegation for a remote graphql schema. And this is how your 119 | schema implementation might look like: 120 | 121 | ```js 122 | import { stitchSchemas } from '@graphql-tools/stitch'; 123 | import typeDefs from './typeDefs'; 124 | import Resolvers from './resolvers'; 125 | import RemoteSchema from './graphCms/schema'; 126 | 127 | export default async () => { 128 | const remoteSchema = await RemoteSchema(); 129 | const resolvers = Resolvers({ subschema: remoteSchema }); 130 | 131 | return stitchSchemas({ 132 | subschemas: [remoteSchema], 133 | typeDefs, 134 | resolvers, 135 | }); 136 | }; 137 | ``` 138 | 139 | ### Neo4J and neo4j-graphql-js 140 | 141 | This is the solution that we're going to cover in the lecture. 142 | 143 | ![Neo4J-GraphQL-JS](../../.github/img/scenario3.png) 144 | 145 | First, get familiar with [Neo4J](https://neo4j.com/) and setup the database 146 | locally. You can either choose to install the database on your host machine, by 147 | following the [installation instructions](https://neo4j.com/docs/operations-manual/current/installation/), 148 | or you could run the database with [docker](https://www.docker.com/): 149 | 150 | ``` 151 | $ docker run --publish=7474:7474 --publish=7687:7687 --env=NEO4J_AUTH=none neo4j:latest 152 | ``` 153 | 154 | In your custom resolvers, you have a couple of options to interact with your 155 | database: 156 | 1. You could use [neo4j-driver](https://github.com/neo4j/neo4j-javascript-driver) 157 | to open a direct connection, 158 | 2. you could use [neode](https://github.com/adam-cowley/neode) which is an 159 | OG-mapper for Neo4J. 160 | 161 | There is an open [pull request at `neo4j-graphql-js`](https://github.com/neo4j-graphql/neo4j-graphql-js/pull/550) which demonstrates how to use schema stitching and schema delegation with 162 | Neo4j. 163 | 164 | Here is how your schema delegation might look like: 165 | 166 | ```js 167 | import { stitchSchemas } from '@graphql-tools/stitch'; 168 | import typeDefs from './typeDefs'; 169 | import Resolvers from './resolvers'; 170 | import localSchema from './neo4j-graphql-js/schema'; 171 | 172 | export default () => { 173 | const resolvers = Resolvers({ subschema: localSchema }); 174 | return stitchSchemas({ 175 | subschemas: [localSchema], 176 | typeDefs, 177 | resolvers 178 | }); 179 | }; 180 | ``` 181 | 182 | 183 | ## Optional Objectives 184 | 185 | :rocket: Implement an aggregated field `postCount`, which returns the number of 186 | posts written by a `User` or `Person`: 187 | ```gql 188 | extend type Person { 189 | postCount: Int 190 | } 191 | ``` 192 | 193 | :rocket: Implement an aggregated field `authored`, which is `true` if the 194 | authenticated user has written a post: 195 | ```gql 196 | extend type Post { 197 | authored: Boolean 198 | } 199 | ``` 200 | 201 | :rocket: Use a DataSource to Access Your Database Directly 202 | 203 | Using [MongoDB](https://www.mongodb.com/) and [apollo-datasource-mongodb](https://github.com/GraphQLGuide/apollo-datasource-mongodb) looks quite straightforward. There is also [sql-datasource](https://github.com/cvburgess/SQLDataSource). 204 | 205 | ![Apollo-Server <--> Database](../../.github/img/scenario1.png) 206 | 207 | :rocket: Use a RESTDataSource to Access a Remote REST API 208 | 209 | You could use `RESTDataSource` as described in the [official documentation](https://www.apollographql.com/docs/apollo-server/data/data-sources/#rest-data-source) 210 | of apollo server. You might want to have a look into this [blog post](https://graphql.org/blog/rest-api-graphql-wrapper/). 211 | 212 | From the architectural point of view, this is similar to a remote [graphql api](#using-a-remote-graphql-api). 213 | Most likely, performance will be worse, because your type resolvers are going to 214 | make additional calls to the endpoint. Yet, this is a nice exercise, because 215 | this is what you're going to do if you face a legacy API. 216 | -------------------------------------------------------------------------------- /exercises/6/README.md: -------------------------------------------------------------------------------- 1 | # Exercise #6 2 | 3 | | Deadline | Date | 4 | | -------------------- | ------------------- | 5 | | **Due date** | **All optional :)** | 6 | 7 | ## Goal 8 | 9 | In this Christmas exercise, it is time to plug frontend, backend and database 10 | together and finally deploy your application to show it to your friends 11 | and families! 12 | 13 | ## Instructions 14 | 15 | 1. Install `vue-apollo` in your frontend and call the backend from there. 16 | Refactor your frontend code so that all data comes from your backend. Certain 17 | groups have already [implemented this](https://github.com/Systems-Development-and-Frameworks/lichtow/tree/origin/main/webapp) 18 | in previous exercises. 19 | 20 | 2. Build your frontend and upload the files to a static webhoster. E.g. you 21 | could use [Netlify](https://www.netlify.com/) or [Surge](https://surge.sh/). 22 | 23 | 3. Build your backend for production. You could e.g. use [Heroku](https://dashboard.heroku.com/apps) 24 | or deploy your backend as a [Serverless](https://www.serverless.com/) function. 25 | There is documentation how to setup `apollo-server` to run on [Heroku](https://www.apollographql.com/docs/apollo-server/deployment/heroku/) 26 | or on a [lambda function](https://www.apollographql.com/docs/apollo-server/deployment/lambda/). 27 | 28 | 4. Use a managed [Neo4J](https://neo4j.com/cloud/) database or a remote GraphQL 29 | API for persistency. If you deploy your backend as a lambda function, I suggest 30 | to use [serverless-dotenv-plugin](https://github.com/colynb/serverless-dotenv-plugin) 31 | to manage credentials. Other helpful plugins are [serverless-offline](https://github.com/dherault/serverless-offline) 32 | for local development and [serverless-bundle](https://github.com/AnomalyInnovations/serverless-bundle) 33 | for ES6 and typescript support. 34 | 35 | 5. Add automatic deployments to your CI/CD pipeline. 36 | 37 | 6. Show-off to your friends and your family! 38 | 39 | **Merry christmas!** 40 | 41 | * , 42 | _/^\_ 43 | < > 44 | * /.-.\ * 45 | * `/&\` * 46 | ,@.*;@, 47 | /_o.I %_\ * 48 | * (`'--:o(_@; 49 | /`;--.,__ `') * 50 | ;@`o % O,*`'`&\ 51 | * (`'--)_@ ;o %'()\ * 52 | /`;--._`''--._O'@; 53 | /&*,()~o`;-.,_ `""`) 54 | * /`,@ ;+& () o*`;-';\ 55 | (`""--.,_0 +% @' &()\ 56 | /-.,_ ``''--....-'`) * 57 | * /@%;o`:;'--,.__ __.'\ 58 | ;*,&(); @ % &^;~`"`o;@(); * 59 | /(); o^~; & ().o@*&`;&%O\ 60 | jgs `"="==""==,,,.,="=="==="` 61 | __.----.(\-''#####---...___...-----._ 62 | '` \)_`"""""` 63 | .--' ') 64 | o( )_-\ 65 | `"""` ` 66 | -------------------------------------------------------------------------------- /exercises/7/README.md: -------------------------------------------------------------------------------- 1 | # Exercise #7 2 | 3 | | Deadline | Date | 4 | | -------------------------- | ---------------------- | 5 | | Review due date (optional) | 20.01.2021 - 14:00 | 6 | | **Final Due date** | **27.01.2021 - 14:00** | 7 | 8 | ## Goal 9 | 10 | Extend exercise [#1](../1), [#2](../2), [#3](../3), [#4](../4), [#5](../5) and 11 | [#6](../6) with the new [objectives](#objectives). 12 | 13 | In this exercise, we will connect our Webapp with the backend. During that, we cover the following topics: 14 | * [Isomorphic JavaScript](https://en.wikipedia.org/wiki/Isomorphic_JavaScript) 15 | * [Client-Side-Rendering(CSR) vs. Server-Side-Rendering(SSR)](https://developers.google.com/web/updates/2019/02/rendering-on-the-web) 16 | * [Vuex](https://vuex.vuejs.org/) 17 | * [Vue-Apollo](https://apollo.vuejs.org/) 18 | * [Progressive-Web-App](https://web.dev/progressive-web-apps/) 19 | 20 | ## Instructions 21 | 22 | 0. Discuss and decide your deployment target with your team: 23 | * Static (JAMstack) or 24 | * Server (Node.js) 25 | 26 | 1. Setup a Nuxt app that replaces your vue-cli `webapp/`. 27 | * Use [create-nuxt-app](https://nuxtjs.org/docs/2.x/get-started/installation#using-create-nuxt-app) 28 | for setup. Make sure to select 29 | * "SSR/SSG" as Rendering mode 30 | * The deployment target you decided on in Instruction #0. 31 | * [@nuxtjs/pwa](https://pwa.nuxtjs.org/) for the progressive web app 32 | * Copy all relevant code (most importantly your components including your specs and stories) from your old `webapp/` folder to your new one. 33 | * Delete the old `webapp/` folder. Make sure the new one is in the same location. 34 | * Commit this refactoring in a *separate* PR and merge it into your `main` branch. This will keep the content of "Files Changed" tab 35 | small and help mentors to review your code. 36 | 37 | 2. Use [@nuxtjs/storybook](https://storybook.nuxtjs.org/) to 38 | setup storybook. 39 | 40 | 3. Use [@nuxtjs/apollo](https://github.com/nuxt-community/apollo-module) to 41 | setup [vue-apollo](https://github.com/vuejs/vue-apollo) in your Nuxt app. An 42 | alternative to `nuxtjs/apollo` is 43 | [nuxt-graphql-request](https://github.com/Gomah/nuxt-graphql-request). Both 44 | of these libraries have 45 | [authentication helpers](https://github.com/nuxt-community/apollo-module#authentication) 46 | or [similar features](https://github.com/Gomah/nuxt-graphql-request#authentication-via-http-header) 47 | to make sure that a valid JWT is sent on every authenticated request. 48 | 49 | 4. Create a `login.vue` page component and a `LoginForm.vue` component. The 50 | login form is responsible to call a `LOGIN` mutation and save the JWT token 51 | returned by the backend. 52 | 53 | 5. Create a menu component with a `` to `/login` if the user is 54 | not logged in. If the user is logged in, it shows a logout button. You might 55 | want to put this this menu component in your `layouts/default.vue`. 56 | Furthermore, you might want to use [Vuex](https://vuex.vuejs.org/) for a 57 | globally accessible `isAuthenticated` getter method. There is a [nuxt integration](https://nuxtjs.org/docs/2.x/directory-structure/store). 58 | 59 | * Hint: Due to a bug, `nuxt-apollo` does not properly read the cookie containing `apollo-token` in SSR. See this [PR](https://github.com/nuxt-community/apollo-module/pull/358). If you need `this.$apolloHelpers.getToken` in SSR you could either follow the PR or parse the cookie like this: 60 | ```js 61 | // in store/index.js 62 | import cookie from 'cookie' 63 | 64 | export const actions = { 65 | nuxtServerInit(store, context) { 66 | const { req } = context.ssrContext 67 | if (!req) return // static site generation 68 | const parsedCookies = cookie.parse(req.headers.cookie) 69 | const token = parsedCookies['apollo-token'] 70 | if (!token) return 71 | store.commit('auth/setToken', token) 72 | }, 73 | } 74 | ``` 75 | * Hint2: You might want to decode the id of the current user from the JWT with [jwt-decode](https://github.com/auth0/jwt-decode). 76 | 77 | 6. Make sure that your `upvote` and `write` mutations hit 78 | your backend. If you use `vue-apollo`, it will update your cache 79 | automatically if you request the `ID` field in your mutations. 80 | 81 | 82 | 7. Your buttons should behave according to the authentication state. E.g. you 83 | could only display `upvote` when the user is logged in. Alternatively, 84 | you could redirect to `/login` if the user is not logged in. 85 | Add a `delete` and `edit` button to your news-entries which only shows for authors. 86 | Connecting them to your backend is optional though. 87 | 88 | 8. PR Review: 89 | * Review a pull request of another team. 90 | * Find at least 6 things (:star: from [Objectives](#objectives)) the other 91 | team did or didn't do. 92 | * Either "Request Changes" or "Approve" *do not just "Comment"*. 93 | * Suggest changes line-by-line in "Files Changed". 94 | * Link to your code review in the description of your own pull request. 95 | * Request a review from another team 96 | 97 | ## Objectives 98 | 99 | :star: For instructions in the `README.md` on how to build your webapp for production. 100 | 101 | :star: For no changes in "Files Changed" tab of the refactoring from `vue-cli` to `create-nuxt-app`. (See #1 in instructions) 102 | 103 | :star: :star: For the API connection between your front- and backend. 104 | 105 | :star: For your previous frontend tests still passing. Requests to the backend are mocked. 106 | 107 | :star: :star: For a login feature in your webapp including a Vue component and its software tests. 108 | 109 | :star: :star: For a menu component which shows a login or logout button and its software tests. 110 | 111 | :star: For an upvote button that behaves according to the authentication state of your user 112 | 113 | :star: For a delete and edit button that is only visible to the author of the post. 114 | 115 | :star: For [Lighthouse](https://developers.google.com/web/tools/lighthouse) reporting that your production website is installable as PWA (except HTTPS). 116 | 117 | :star: For requesting a review and reviewing another team's PR. 118 | 119 | All objectives must be implemented according to the [instructions](#instructions). 120 | 121 | ## Optional Objectives 122 | 123 | :rocket: Create a storybook story for `LoginForm.vue`. 124 | 125 | :rocket: Create storybook stories to show the appearance of the post component to the author and to another users. 126 | 127 | :rocket: Use different [layouts](https://nuxtjs.org/docs/2.x/directory-structure/layouts). E.g. add a logout button in `layouts/default.vue`. Use a different layout for `pages/login.vue`. 128 | 129 | :rocket: Add a [middleware](https://nuxtjs.org/docs/2.x/directory-structure/middleware) to redirect from `/login` to `/` when already logged in. 130 | 131 | :rocket: Add [dynamic page components](https://nuxtjs.org/examples/routing-dynamic-pages/). E.g. every post gets a separate page component, e.g. `/post/_id.vue` or `/post/_slug.vue`. 132 | 133 | :rocket: Navigating to a non-existing post route responds with a 404 HTTP status code. 134 | 135 | :rocket: Your menu component shows the name of the current user when logged in. You could e.g. call a another grapqhl query to get the name of the user after a successful login. Alternatively, you could encode the name of the user in the JWT. 136 | 137 | :rocket: The form to submit a new post has another text input for the URL of a link. 138 | 139 | :rocket: The URL of the post appears on the post component or post page as an external link. 140 | 141 | :rocket: On every post page you can see the list of voters. It's up to you if the result of the vote (up or down) is made public. 142 | -------------------------------------------------------------------------------- /exercises/8/README.md: -------------------------------------------------------------------------------- 1 | # Exercise #8 2 | 3 | | Deadline | Date | 4 | | -------------------------- | ---------------------- | 5 | | **Final Due date** | **03.02.2021 - 14:00** | 6 | 7 | ## Goal 8 | 9 | In this exercise we practice how to implement a design specification given in the form of an image or document. You do not necessarily have to extend exercise 1-7 (this is optional, see [Optional Objectives](#optional-objectives)). Make it clear in your Pull Requests how we can build and test your solution. 10 | 11 | The goal is to implement a simplified [Holy Grail layout](https://en.wikipedia.org/wiki/Holy_grail_(web_design)) with a header, content and footer section. Header and content must be responsive. It is advised to make use of [media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries) for responsiveness and [CSS grids](https://developer.mozilla.org/de/docs/Web/CSS/CSS_Grid_Layout) for the layout. 12 | 13 | ## Instructions 14 | 15 | 16 | Discuss and decide in your team if you want to use a framework: 17 | * No framework, instead plain HTML/CSS (best for learning) 18 | * [TailwindCSS](https://tailwindcss.com/) - [nuxt-module](https://tailwindcss.nuxtjs.org/) (balance between flexibility and convenience) 19 | * [Vuetify](https://vuetifyjs.com/) - [nuxt-module](https://github.com/nuxt-community/vuetify-module) (most convenient solution) 20 | * Another framework like Bootstrap Vue, Buefy, Vue Material etc. Note that the mentors can only offer limited support on these solutions. 21 | 22 | 23 | 24 | Extend your hackernews app **or** setup a new project with the framework you choose and start implementing header, content and footer. 25 | 26 | For big screens, the header must show all navigation links listed at the top. For smaller screens it must use a [hamburger menu](https://en.wikipedia.org/wiki/Hamburger_button) which reveals a [sidebar](https://en.wikipedia.org/wiki/Sidebar_(computing)) to show the same links. 27 | 28 | For big screens, the content section must use a three column layout. For smaller screens it must use one column to show the same content. 29 | The content section contains content items which constist of at least a title and a text. They have to change responsively as well. For big screens, the content items must use a 1-column layout. For smaller screens it must switch to a 2-column layout. (see images) 30 | 31 | The footer section does not have to be responsive per se, but it must look good on both big and small screens. The content of the footer is up to you, but it must contain at least some text. 32 | 33 | > Big screens/ big screens: laptops and larger 34 | > 35 | > Small screens/smaller screens: everything smaller than laptops (tablets and smartphones) 36 | 37 | Here is how the website should look like: 38 | 39 | ### Large screen 40 | 41 | 42 | ### Small screen 43 | 44 | 45 | ### Small screen with revealed sidebar 46 | 47 | 48 | ## Objectives 49 | 50 | :star: For a header and it's responsiveness (direct links vs. hamburger menu) 51 | 52 | :star: For a content section and it's content items 53 | 54 | :star: For a responsive content section (3-column vs 1-column layout) 55 | 56 | :star: For responsive content items (1-column vs. 2-column layout) 57 | 58 | :star: For a footer 59 | 60 | All objectives must be implemented according to the [instructions](#instructions). 61 | 62 | ## Optional Objectives 63 | 64 | :rocket: For extending exercise 1-7 (instead of creating a new project) and adapting the content section (to contain title and votes) 65 | 66 | :rocket: For requesting a review and reviewing another team's PR. 67 | 68 | :rocket: For using [BEM](https://getbem.com/) or [rscss](https://rscss.io/) or a similar CSS ruleset to keep your CSS specificity low. 69 | 70 | :rocket: For a sidebar containing all the navigation links. The sidebar is revealed when the hamburger menu button is clicked and can be closed. 71 | 72 | :rocket: For a smooth transition when the sidebar is revealed or hidden. Use [VueJS transitions](https://vuejs.org/v2/guide/transitions.html). 73 | 74 | :rocket: For using [semantic html](https://medium.com/@zac_heisey/7-alternatives-to-the-div-html-tag-7c888c7b5036). 75 | -------------------------------------------------------------------------------- /exercises/README.md: -------------------------------------------------------------------------------- 1 | # Exercises folder 2 | 3 | * [Exercise #1](./1/README.md) 4 | * [Exercise #2](./2/README.md) 5 | * [Exercise #3](./3/README.md) 6 | * [Exercise #4](./4/README.md) 7 | * [Exercise #5](./5/README.md) 8 | * [Exercise #6](./6/README.md) 9 | * [Exercise #7](./7/README.md) 10 | * [Exercise #8](./8/README.md) 11 | 12 | 13 | --------------------------------------------------------------------------------