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

feat: otter sdk training - how to use the otter sdk #2493

Merged
merged 1 commit into from
Nov 25, 2024
Merged
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
5 changes: 5 additions & 0 deletions apps/showcase/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@
"glob": "*.json",
"input": "packages/@o3r-training/training-sdk/dist/structure",
"output": "/assets/@o3r-training/training-sdk/structure"
},
{
"glob": "*.json",
"input": "packages/@o3r-training/showcase-sdk/dist/structure",
kpanot marked this conversation as resolved.
Show resolved Hide resolved
"output": "/assets/@o3r-training/showcase-sdk/structure"
}
],
"styles": [
Expand Down
35 changes: 34 additions & 1 deletion apps/showcase/src/assets/trainings/sdk/program.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,40 @@
},
{
"stepTitle": "How to use the Otter SDK?",
"htmlContentUrl": "./steps/typescript-sdk/instructions.md"
"htmlContentUrl": "./steps/typescript-sdk/instructions.md",
"filesConfiguration": {
"name": "how-to-use-otter-sdk",
"startingFile": "apps/tutorial-app/src/app/app.component.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/showcase-sdk/structure/src.json"
},
{
"path": "./apps/tutorial-app/src/app",
"contentUrl": "./steps/typescript-sdk/exercise.json"
}
],
"solutionUrls": [
{
"path": "./apps/tutorial-app/src/app",
"contentUrl": "./steps/typescript-sdk/solution.json"
}
],
"mode": "interactive",
"commands": [
"npm install --legacy-peer-deps --ignore-scripts --force",
"npm run ng run tutorial-app:serve"
]
}
},
{
"stepTitle": "Customize your fetch client with plugins",
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<table class="table">
<caption align="top">
First 10 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,26 @@
import { Component, inject, signal } from '@angular/core';
import { type Pet, PetApi } 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';

Comment on lines +12 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** Title of the application */
public title = 'tutorial-app';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The title variable is currently being used in the test file app.component.spec.ts, we can update this test file eventually or remove them for the case of this training (but in a separate PR)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are not running the test and if we are it will fail :)
but yes we can do it in another PR

private readonly petStoreApi = inject(PetApi);
readonly #pets = signal<Pet[]>([]);
sdo-1A marked this conversation as resolved.
Show resolved Hide resolved
public readonly pets = this.#pets.asReadonly();

constructor() {
void this.setPets();
}

public async setPets() {
/* Get the first 10 pets whose status is 'available' */
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ApiFetchClient } from '@ama-sdk/client-fetch';
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { PetApi } from 'sdk';
import { routes } from './app.routes';


function petApiFactory() {
/* Create an ApiFetchClient and return a PetApi object */
}

export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
{provide: PetApi, useFactory: petApiFactory}
]
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
### Objective
For this example, you will use the public <a href="https://petstore3.swagger.io/" target="_blank">Swagger Petstore project API</a>.
You will perform a simple call and retrieve a list of pets from an SDK programmatically generated from their public specification.
Since this step requires a Java setup, the generation has already been done for you. You will just need to integrate the Otter SDK client and perform your call.

### Exercise

#### Creation of fetch client in the application configuration
Let's create a fetch client in your application and use it to access your API.\
Here are a couple of steps and hints to help you:
- In the file `app.config.ts`, you will create an API client object of type `ApiFetchClient` from `@ama-sdk/client-fetch` in the existing function `petApiFactory()`.
- The constructor of `ApiFetchClient` requires some options, including the `basePath` which should be the Swagger Petstore API: https://petstore3.swagger.io/api/v3
- The configuration variable can then be used to create an API object of type `PetApi` from the SDK.
- The function `petApiFactory()` should return this `PetApi` object, which has been added to the providers of the application configuration.

#### Using the API object to perform an HTTP request
Now, you can call the API object to perform the HTTP request you are looking for.\
As you can see in `app.component.ts`, the `PetApi` has been injected and is ready to be used.\
For this exercise, you will look for the pets that are of status **available** and display in the UI the names of the first ten pets.

> [!TIP]
> Have a look at the `sdk/src/api/pet/pet.api.ts` file and look for `findPetsByStatus`.

> [!NOTE]
> You can check out the exercise solution or compare your answer to the result of the petstore API: https://petstore3.swagger.io/api/v3/pet/findByStatus?status=available
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Component, inject, signal } from '@angular/core';
import { type Pet, PetApi } 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';

Comment on lines +12 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** Title of the application */
public title = 'tutorial-app';

private readonly petStoreApi = inject(PetApi);
readonly #pets = signal<Pet[]>([]);
sdo-1A marked this conversation as resolved.
Show resolved Hide resolved
public readonly pets = this.#pets.asReadonly();

constructor() {
void this.setPets();
}

public async setPets() {
/* Get the first 10 pets whose status is 'available' */
const pets = await this.petStoreApi.findPetsByStatus({status: 'available'});
this.#pets.set(pets.slice(0, 10));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ApiFetchClient } from '@ama-sdk/client-fetch';
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { PetApi } from 'sdk';
import { routes } from './app.routes';


function petApiFactory() {
/* Create an ApiFetchClient and return a PetApi object */
const apiFetchClient = new ApiFetchClient(
{
basePath: 'https://petstore3.swagger.io/api/v3',
requestPlugins: [],
fetchPlugins: []
}
);
return new PetApi(apiFetchClient);
}

export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
{provide: PetApi, useFactory: petApiFactory}
]
};
10 changes: 10 additions & 0 deletions packages/@o3r-training/showcase-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,22 @@
},
"./openapi.yml": {
"default": "./openapi.yml"
},
"./structure/src.json": {
"default": "./structure/src.json"
},
"./structure/spec.json": {
"default": "./structure/spec.json"
}
},
"scripts": {
"clean": "rimraf test/ test-dev/ dist/ dist-dev/ dist-test/ build/",
"lint:ci": "eslint '**/*[jt]s' --quiet --format junit --output-file ./dist-lint/result.xml",
"lint": "eslint '**/*[jt]s' --cache",
"start": "tsc-watch -b tsconfigs/esm2020 --noClear --onFirstSuccess \"yarn run files:pack --watch\"",
"extract": "yarn run extract-src && yarn run extract-spec",
"extract-src": "o3r-extract-folder-structure --files \"src\" -o dist/structure/src.json",
"extract-spec": "o3r-extract-folder-structure --files \"./openapi.yml\",\"./openapitools.json\" -o dist/structure/spec.json",
"build": "yarn run build:cjs && yarn run build:esm2015 && yarn run build:esm2020 && cpy openapi.yml ./dist && yarn run files:pack",
"build:cjs": "swc src -d dist/cjs -C module.type=commonjs -q",
"build:esm2015": "swc src -d dist/esm2015 -C module.type=es6 -q",
Expand Down Expand Up @@ -81,6 +90,7 @@
"@commitlint/config-conventional": "^19.0.0",
"@nx/eslint-plugin": "~19.5.0",
"@nx/jest": "~19.5.0",
"@o3r-training/training-tools": "workspace:^",
"@o3r/eslint-config-otter": "workspace:^",
"@o3r/eslint-plugin": "workspace:^",
"@openapitools/openapi-generator-cli": "~2.15.0",
Expand Down
18 changes: 17 additions & 1 deletion packages/@o3r-training/showcase-sdk/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"targets": {
"build": {
"executor": "nx:noop",
"dependsOn": ["compile"]
"dependsOn": ["compile", "extract-folder-structure"]
},
"compile": {
"executor": "nx:run-script",
Expand Down Expand Up @@ -36,6 +36,22 @@
"options": {
"command": "npm publish packages/@o3r-training/showcase-sdk/dist"
}
},
"extract-folder-structure": {
"cache": true,
"executor": "nx:run-script",
"options": {
"script": "extract"
},
"inputs": [
sdo-1A marked this conversation as resolved.
Show resolved Hide resolved
"source",
"^cli",
"{projectRoot}/package.json",
"{projectRoot}/openapi.yml",
"{projectRoot}/openapitools.json"
],
"outputs": ["{projectRoot}/dist/structure"],
"dependsOn": ["^build", "compile"]
}
},
"tags": ["showcase"]
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7033,6 +7033,7 @@ __metadata:
"@commitlint/config-conventional": "npm:^19.0.0"
"@nx/eslint-plugin": "npm:~19.5.0"
"@nx/jest": "npm:~19.5.0"
"@o3r-training/training-tools": "workspace:^"
"@o3r/eslint-config-otter": "workspace:^"
"@o3r/eslint-plugin": "workspace:^"
"@openapitools/openapi-generator-cli": "npm:~2.15.0"
Expand Down