Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Importing the application #2

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
This workshop aims to spread coding best practices to young developers (interns, < 3y XP ,..). For instance, we could provide exercices about logging or error handling best practices.
Through a real life inspired application, the trainees could deal with coding basics: clean code, OOP principles, TDD and such like (see below) while submitting their first Pull Request.

They could do it either alone or ( it's strongly recommended) using the pair programming technique.
They could do it either alone or ( it's strongly recommended) using the pair programming technique.


## Workshop

Expand Down
Binary file added docs/media/confucius.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/fire-leaving.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/money.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/movies.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
138 changes: 138 additions & 0 deletions docs/workshop.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,3 +430,141 @@ Implementations of those methods are implemented one level to the right of the m
## Log management

## Git (conventional commits)

## Code quality in action : workshop objectives & rules

<img src="media/confucius.jpeg" alt="Confucius" width="1620" height="400"/>

> I hear and I forget. I see and I remember. I do and I understand.

This workshop is designed to apply the principles of clean code and best practices in a real-life scenario.
It relies on a fictional application, which have many code qualities issues, will make you experience the pain of bad code quality, and the benefits of good practices.
The objective is to implement the requirements and improve the quality, not to over-think every aspect.

## Workshop setup & timetable

The dvd-store web-app is powered by Quarkus, Java 17 & H2 Database.

<ins>How to run the project locally ?</ins>
* Checkout the repo https://github.com/worldline/code-quality-workshop.git
* Create your local branch
* Open the **pom.xml** located in **dvd-store** sub-folder with your favorite IDE
* Run the application

Either use the dvd-store run configuration, create a new Quarkus run configuration, or launch via mvn goal.
``` script
compile quarkus:dev
```

The default application port is 8082, customizable in application.properties.
The database is reset at startup, see class `DataInitTool` to see how a minimal dataset is created.

Several REST APIs are available, documented [here](http://localhost:8082/q/swagger-ui).

Some assumptions you can make :
* No need to care about DB constraints
* No Null Entity or fields
* If you have trouble doing proper DB requests, it is fine to load all entries of a table, then filter it in Java. The objective
is not to master https://quarkus.io/guides/hibernate-orm-panache .

You can expect to finish the requirements in 2-4 hours, depending on your experience and the refactoring effort you put in.


However, it is recommended to stay with Quarkus, Java 17 and H2 :-)

## Setting the stage : DVD store

This project is an app for a video/DVD renting company, and was developed by (too) many developers. Some of them were part of the owner's family ... and not even developers.
Hence quality and long-term vision are not the motto for this app. But this stops now !

The company has recently been bought, and the new owner got genius ideas to make the business great again.
As hiring a PO was too expensive, and his job can't be too complicated, the owner of the shop will be your PO for the day.

Fortunately, your team has been selected to make his dreams a reality, implement features and increase the overall quality.
You're allowed to modify anything you want:
* Add new class, new package
* Change the data model
* Correct any problems you see
* Freedom to organize yourself : could be a good opportunity to do https://en.wikipedia.org/wiki/Pair_programming .


### Ready , set.... go !

## Requirements

<img src="media/fire-leaving.jpg" alt="Leaving a burning project" width="1620" height="400"/>


#### It is strongly recommended to implement them in order, as the complexity increases.

#### Don't worry if you don't have enough time to finish : Focus on the journey, not the destination

* Requirement 0 is not very precise and there is several options to implement it, but introducing Movie object will be a huge benefit for the next requirement.
* Requirement 1 is tougher as you will discover lots of problems, hence lots of refactoring on top of the requirement itself

To help you, some hints are available in the requirements, but don't hesitate to ask for more.

### Requirement 0 : Refactor and introduce Movie concept

<div style="display: flex; align-items: center;">
<img src="media/movies.jpg" alt="Example image" width="80" height="80" style="margin-right: 10px;"/>
<p style="margin-bottom: 0; margin-top: 0;">The future is in content... so the business doesn't know the difference between a DVD and a VideoTape... everything you can watch will now be a Movie !
All next requirements will be for Movies from now on</p>
</div>

<details> <summary>Hint</summary>
Movie could be a new interface,or a new abstract class, you may also need a new enum for the type of movie (DVD, VideoTape).
This is a good opportunity to get rid of all the petty details slowing you down:

* Confusing names
* Lack of unit-tests
* Language barrier
</details>


### Requirement 1 : new pricing logic


<div style="display: flex; align-items: center;">
<img src="media/money.jpg" alt="Example image" width="80" height="80" style="margin-right: 10px;"/>
<p style="margin-bottom: 0; margin-top: 0;"> We're running out of business because we're not making enough money!
We're gonna change the way we price :
The quality of a DVD will drop down with each renting..... so the value should decrease as well.</p>
</div>

##### Here are the pricing rules :

* Price for a DVD = 10 if it was released more than 10 years ago
* Actual logic other case , max price is 23 for any movie
* Oh, and I'm a huge fan of Tom Cruise, every movie is a masterpiece ! Then price when Tom is within a movie should be 15

Add an API that gives the price for a movie ( ISBN ), provide the two prices DVD / VideoTape if possible.

#### Response example

```json
{
DVDPrice : xxx,
VideoPrice : yyy
}
```
OR
```json
[
{ price : xxx, type: DVD },
{ price : yyy, type : Video }
]
```
<details> <summary>Hint</summary>
Add unit-test for the existing and the new pricing rules.
I'm not sure the owner realized some of his instructions are contradictory...
As he's in holiday in the Bahamas, you have to decide what to do with the Tom Cruise rule.
</details>

## Conclusion

An implementation of the requirements is available in the branch `TODO`, and there are many way to compare what you did with the solution.
* Open a PR with your branch, and compare the changes.
* Use a diff tool to compare your code with the solution

We hope that you enjoyed this workshop, and that you learned a lot about clean code and best practices.
5 changes: 5 additions & 0 deletions dvd-store/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*
!target/*-runner
!target/*-runner.jar
!target/lib/*
!target/quarkus-app/*
39 changes: 39 additions & 0 deletions dvd-store/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
release.properties

# Eclipse
.project
.classpath
.settings/
bin/

# IntelliJ
.idea
*.ipr
*.iml
*.iws

# NetBeans
nb-configuration.xml

# Visual Studio Code
.vscode
.factorypath

# OSX
.DS_Store

# Vim
*.swp
*.swo

# patch
*.orig
*.rej

# Local environment
.env
142 changes: 142 additions & 0 deletions dvd-store/.mvn/wrapper/MavenWrapperDownloader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;

public class MavenWrapperDownloader
{
private static final String WRAPPER_VERSION = "3.1.0";

/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL =
"https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/" + WRAPPER_VERSION
+ "/maven-wrapper-" + WRAPPER_VERSION + ".jar";

/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to use instead of the
* default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH = ".mvn/wrapper/maven-wrapper.properties";

/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH = ".mvn/wrapper/maven-wrapper.jar";

/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";

public static void main( String args[] )
{
System.out.println( "- Downloader started" );
File baseDirectory = new File( args[0] );
System.out.println( "- Using base directory: " + baseDirectory.getAbsolutePath() );

// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File( baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH );
String url = DEFAULT_DOWNLOAD_URL;
if ( mavenWrapperPropertyFile.exists() )
{
FileInputStream mavenWrapperPropertyFileInputStream = null;
try
{
mavenWrapperPropertyFileInputStream = new FileInputStream( mavenWrapperPropertyFile );
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load( mavenWrapperPropertyFileInputStream );
url = mavenWrapperProperties.getProperty( PROPERTY_NAME_WRAPPER_URL, url );
}
catch ( IOException e )
{
System.out.println( "- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'" );
}
finally
{
try
{
if ( mavenWrapperPropertyFileInputStream != null )
{
mavenWrapperPropertyFileInputStream.close();
}
}
catch ( IOException e )
{
// Ignore ...
}
}
}
System.out.println( "- Downloading from: " + url );

File outputFile = new File( baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH );
if ( !outputFile.getParentFile().exists() )
{
if ( !outputFile.getParentFile().mkdirs() )
{
System.out.println( "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath()
+ "'" );
}
}
System.out.println( "- Downloading to: " + outputFile.getAbsolutePath() );
try
{
downloadFileFromURL( url, outputFile );
System.out.println( "Done" );
System.exit( 0 );
}
catch ( Throwable e )
{
System.out.println( "- Error downloading" );
e.printStackTrace();
System.exit( 1 );
}
}

private static void downloadFileFromURL( String urlString, File destination )
throws Exception
{
if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null )
{
String username = System.getenv( "MVNW_USERNAME" );
char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray();
Authenticator.setDefault( new Authenticator()
{
@Override
protected PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication( username, password );
}
} );
}
URL website = new URL( urlString );
ReadableByteChannel rbc;
rbc = Channels.newChannel( website.openStream() );
FileOutputStream fos = new FileOutputStream( destination );
fos.getChannel().transferFrom( rbc, 0, Long.MAX_VALUE );
fos.close();
rbc.close();
}

}
18 changes: 18 additions & 0 deletions dvd-store/.mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
33 changes: 33 additions & 0 deletions dvd-store/.runConfiguration/dvd-store.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="dvd-store" type="QuarkusRunConfigurationType" factoryName="Quarkus">
<module name="dvd-store" />
<QsMavenRunConfiguration>
<MavenSettings>
<option name="myGeneralSettings" />
<option name="myRunnerSettings" />
<option name="myRunnerParameters">
<MavenRunnerParameters>
<option name="profiles">
<set />
</option>
<option name="goals">
<list>
<option value="quarkus:dev" />
</list>
</option>
<option name="pomFileName" value="pom.xml" />
<option name="profilesMap">
<map />
</option>
<option name="resolveToWorkspace" value="false" />
<option name="workingDirPath" value="$PROJECT_DIR$" />
</MavenRunnerParameters>
</option>
</MavenSettings>
<targetMavenLocalRepo />
</QsMavenRunConfiguration>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
Loading
Loading