Skip to content

Commit

Permalink
feat: otter sdk training - integration in angular
Browse files Browse the repository at this point in the history
  • Loading branch information
sdo-1A committed Nov 29, 2024
1 parent 5f1b7cf commit 387a786
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 3 deletions.
19 changes: 16 additions & 3 deletions apps/showcase/src/assets/trainings/sdk/program.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,34 @@
"htmlContentUrl": "./steps/angular-integration/instructions.md",
"filesConfiguration": {
"name": "angular-integration",
"startingFile": "apps/tutorial-app/src/app/app.component.ts",
"startingFile": "apps/tutorial-app/src/app/app.config.ts",
"urls": [
{
"path": ".",
"contentUrl": "./shared/monorepo-template.json"
},
{
"path": "./libs/sdk",
"contentUrl": "@o3r-training/showcase-sdk/structure/spec.json"
},
{
"path": "./libs/sdk/src",
"contentUrl": "@o3r-training/training-sdk/structure/src.json"
"contentUrl": "@o3r-training/showcase-sdk/structure/src.json"
},
{
"path": "./apps/tutorial-app/src/app",
"contentUrl": "./steps/angular-integration/exercise.json"
}
],
"solutionUrls": [
{
"path": "./apps/tutorial-app/src/app",
"contentUrl": "./steps/angular-integration/solution.json"
}
],
"mode": "interactive",
"commands": [
"npm install --legacy-peer-deps --ignore-scripts --force",
"npm run ng run sdk:build",
"npm run ng run tutorial-app:serve"
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class="d-flex gap-3 mt-3">
<button class="btn btn-outline-primary" (click)="getAvailablePets()">Get Available Pets</button>
<button class="btn btn-outline-primary" (click)="getPetInventory()">Get Inventory</button>
</div>
<table class="table">
<caption align="top">
Pets with the status <b>available</b> from the Swagger Petstore
</caption>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
@for (pet of pets(); track pet.id; let index = $index) {
<tr>
<td>{{ index + 1 }}</td>
<td>{{ pet.name }}</td>
<td>{{ pet.status }}</td>
</tr>
}
</tbody>
</table>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Component, inject, signal } from '@angular/core';
import { ApiFactoryService } from '@o3r/apis-manager';
import { type Pet, PetApi, StoreApi } from 'sdk';

@Component({
selector: 'app-root',
standalone: true,
imports: [],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
/** Title of the application */
public title = 'tutorial-app';

/* Inject the ApiFactoryService and get the corresponding APIs */
private petApi;
private storeApi;

private readonly petsWritable = signal<Pet[]>([]);
public readonly pets = this.petsWritable.asReadonly();

private readonly petsInventoryWritable = signal<{ [key: string]: number }>({});
public readonly petsInventory = this.petsInventoryWritable.asReadonly();

public async getAvailablePets() {
/* Get the pets whose status is 'available' */
}

public async getPetInventory() {
/* Get the pets inventory */
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ApiFetchClient } from '@ama-sdk/client-fetch';
import { ApiClient, PluginRunner, RequestOptions, RequestPlugin } from '@ama-sdk/core';
import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { ApiManager, ApiManagerModule } from '@o3r/apis-manager';
import { routes } from './app.routes';

class RequestLogPlugin implements RequestPlugin {
public load(): PluginRunner<RequestOptions, RequestOptions> {
return {
transform: (data: RequestOptions) => {
alert(JSON.stringify(data));
return data;
}
};
}
}

// Default configuration for all the APIs defined in the ApiManager
const apiConfig: ApiClient = new ApiFetchClient(
// Properties of ApiFetchClient
);
const apiManager = new ApiManager(apiConfig, {
// Configuration override for a specific API
});

export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
importProvidersFrom(ApiManagerModule.forRoot(apiManager))
]
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
When dealing with an Angular project, you need to ensure that your `ApiClient` will be shared accross
your application. The Otter framework provides the `ApiManager` service to manage your API collection.

### Objective
- Leverage the `ApiManager` service to access two different clients to retrieve the list of available pets and submit an order for the first pet returned.
- Add a plugin to the `OrderApi` to log each time a call is sent.

### Prerequisite
- The package `@o3r/apis-manager` needs to be installed in the project with `npm install @o3r/apis-manager` (which has already been done for you).

### Exercise
As you can see in the `app.config.ts` file, a plugin `RequestLogPlugin` has been created which displays an alert box when the API receives a request.

Integrate the `ApiManagerModule` in your `ApplicationConfig` and configure it to use the `RequestLogPlugin` in the `StoreApi`.
You can inspire yourself with the following lines:

```typescript
// Default configuration for all the APIs defined in the ApiManager
const apiConfig: ApiClient = new ApiFetchClient(
{
// Properties of ApiFetchClient
}
);
const apiManager = new ApiManager(apiConfig, {
// Configuration override for a specific API
StoreApi: new ApiFetchClient({
// Properties of ApiFetchClient
})
});

export const appConfig: ApplicationConfig = {
providers: [importProvidersFrom(ApiManagerModule.forRoot(apiManager))]
};
```

Now, checkout the `app.component.ts` file and inject the `ApiFactoryService` to use your unique instance of the `StoreApi` and `PetApi`.
In the existing functions, update the `pets` signal with the result of a call to `findPetsByStatus` and the `petsInventory` signal with the the result of `getInventory`.

Now, when clicking the **Get Available Pets** button, your table should be updated with the list of available pets (without the alert box).
When clicking the **Get Inventory** button, you should see the request to the `StoreApi` logged in the alert box.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Component, inject, signal } from '@angular/core';
import { ApiFactoryService } from '@o3r/apis-manager';
import { type Pet, PetApi, StoreApi } from 'sdk';

@Component({
selector: 'app-root',
standalone: true,
imports: [],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
/** Title of the application */
public title = 'tutorial-app';

/* Inject the ApiFactoryService and get the corresponding APIs */
private petApi = inject(ApiFactoryService).getApi(PetApi);
private storeApi = inject(ApiFactoryService).getApi(StoreApi);

private readonly petsWritable = signal<Pet[]>([]);
public readonly pets = this.petsWritable.asReadonly();

private readonly petsInventoryWritable = signal<{ [key: string]: number }>({});
public readonly petsInventory = this.petsInventoryWritable.asReadonly();

public async getAvailablePets() {
/* Get the pets whose status is 'available' */
const availablePets = await this.petApi.findPetsByStatus({status: 'available'});
this.petsWritable.set(availablePets);
}

public async getPetInventory() {
/* Get the pets inventory */
const inventory = await this.storeApi.getInventory({});
this.petsInventoryWritable.set(inventory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ApiFetchClient } from '@ama-sdk/client-fetch';
import { ApiClient, PluginRunner, RequestOptions, RequestPlugin } from '@ama-sdk/core';
import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { ApiManager, ApiManagerModule } from '@o3r/apis-manager';
import { routes } from './app.routes';

class RequestLogPlugin implements RequestPlugin {
public load(): PluginRunner<RequestOptions, RequestOptions> {
return {
transform: (data: RequestOptions) => {
alert(JSON.stringify(data));
return data;
}
};
}
}

// Default configuration for all the APIs defined in the ApiManager
const apiConfig: ApiClient = new ApiFetchClient(
{
basePath: 'https://petstore3.swagger.io/api/v3',
requestPlugins: [],
fetchPlugins: []
}
);
const apiManager = new ApiManager(apiConfig, {
// Configuration override for a specific API
StoreApi: new ApiFetchClient({
basePath: 'https://petstore3.swagger.io/api/v3',
requestPlugins: [new RequestLogPlugin()],
fetchPlugins: []
})
});

export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
importProvidersFrom(ApiManagerModule.forRoot(apiManager))
]
};

0 comments on commit 387a786

Please sign in to comment.