Aller au contenu principal

Intégration

Pendant les séances d'intégration, il n'y a pas de nouveau contenu. Le but est de consolider ce que tu as vu dans les séances précédentes.

Exercices à compléter.

Tu dois compléter le TP2.

Exercice complémentaire

Exercice préparatoire : Trace client-serveur

Contexte : Le code suivant met en place une interaction client-serveur entre une application Android et un serveur Spring Boot. L’objectif est de permettre l’affichage des détails d’une tâche à partir de son identifiant (id). L’interaction est initiée lorsque l’utilisateur sélectionne une tâche dans l'application.

Code client :

data class TaskDetailResponse(
val id: Long,
val name: String,
val deadline: Date,
val events: List<ProgressEvent>,
val percentageDone: Int,
val percentageTimeSpent: Double
)


interface Service {
@GET("/api/detail/{id}")
fun detail(@Path("id") id: Long) : Call<TaskDetailResponse>
}


object RetrofitUtil {
private var instance: Service? = null
fun get(): Service {
if (instance == null) {
val retrofit = Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(CustomGson.getIt()))
.client(client())
.baseUrl("http://10.0.2.2:8080/")
.build()
instance = retrofit.create<Service>(Service::class.java)
return instance!!
} else{
return instance!!
}
}

private fun client(): OkHttpClient {
return OkHttpClient.Builder()
.cookieJar(SessionCookieJar)
.build()
}

}

class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
AppNavigation()
}
}
}

@Composable
fun AppNavigation() {
val navController = rememberNavController()

NavHost(
navController = navController,
startDestination = "home"
) {

composable("home") {
HomeScreen(navController)
}

composable("details/{id}") { backStackEntry ->
val id = backStackEntry.arguments?.getString("id")?.toLong() ?: 0
DetailsScreen(id)
}
}
}

@Composable
fun DetailsScreen(id: Long) {

var task by remember { mutableStateOf<TaskDetailResponse?>(null) }

val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()

LaunchedEffect(id) {

val service: Service = RetrofitUtil.get()

service.detail(id).enqueue(object : Callback<TaskDetailResponse> {

override fun onResponse(
call: Call<TaskDetailResponse>,
response: Response<TaskDetailResponse>
) {
if (response.isSuccessful) {
task = response.body()
} else {
scope.launch {
snackbarHostState.showSnackbar("Erreur serveur : ${response.code()}")
}
}
}

override fun onFailure(call: Call<TaskDetailResponse>, t: Throwable) {
scope.launch {
snackbarHostState.showSnackbar("Erreur réseau")
}
}
})
}
}

Code serveur :

@Controller
public class ControllerTask {

@Autowired
private ServiceTask serviceTask;

@GetMapping("/api/detail/{id}")
public @ResponseBody TaskDetailResponse detail(@PathVariable long id) {
System.out.println("KICKB SERVER : Detail with cookie ");
MUser user = currentUser(); // Décrire uniquement l'effet global de cette ligne
return serviceTask.detail(id, user);
}

private MUser currentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
System.out.println("Le nom utilisateur est " + username);
UserDetails ud = (UserDetails) authentication.getPrincipal();
return serviceTask.userFromUsername(ud.getUsername());
}
}

Étant données les premières étapes suivantes, produisez la trace d'exécution décrivant les principales étapes de la communication client-serveur:

  1. Le serveur Spring Boot est lancé localement;
  2. L'utilisateur démarre l'application Android;
  3. L'utilisateur s'inscrit et crée une première tâche. De retour à l'écran d'accueil, il sélectionne la tâche nouvellement créée pour en consulter les détails.
ligne exécutéeeffetpile d'appels