mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
Merge pull request #464 from solon/grammar-tweaks
README grammar tweaks
This commit is contained in:
commit
e9c9f76539
189
README.md
189
README.md
@ -47,9 +47,9 @@ to javascript object's properties
|
||||
* create one-to-one, many-to-one, one-to-many, many-to-many relations between tables
|
||||
* and much more...
|
||||
|
||||
TypeORM uses Data Mapper pattern, unlike all other JavaScript ORMs that
|
||||
TypeORM uses the Data Mapper pattern, unlike all other JavaScript ORMs that
|
||||
currently exist, which means you can write loosely coupled, scalable,
|
||||
maintainable applications with less problems.
|
||||
maintainable applications with fewer problems.
|
||||
|
||||
The benefit of using TypeORM for the programmer is the ability to focus on
|
||||
the business logic and worry about persistence only as a secondary problem.
|
||||
@ -97,14 +97,14 @@ TypeORM is highly influenced by other ORMs, such as [Hibernate](http://hibernate
|
||||
|
||||
`npm install oracledb --save`
|
||||
|
||||
Install only one of them, depend on which database you use.
|
||||
Install only one of them, depending on which database you use.
|
||||
|
||||
To make oracle driver to work you need to follow installation instructions from
|
||||
To make the Oracle driver work, you need to follow the installation instructions from
|
||||
[their](https://github.com/oracle/node-oracledb) site.
|
||||
|
||||
#### TypeScript configuration
|
||||
|
||||
Also make sure you are using TypeScript compiler version > **2.1** and you have enabled following settings in `tsconfig.json`:
|
||||
Also make sure you are using version **2.1** or greater of the TypeScript compiler, and you have enabled the following settings in `tsconfig.json`:
|
||||
|
||||
```json
|
||||
"emitDecoratorMetadata": true,
|
||||
@ -122,13 +122,13 @@ If you have errors during app bootstrap, try to upgrade your Node.js version to
|
||||
|
||||
TypeORM works in the browser and has experimental support of WebSQL.
|
||||
If you want to use TypeORM in the browser then you need to `npm i typeorm-browser` instead of `typeorm`.
|
||||
More information about it in [this page](https://typeorm.github.io/usage-in-browser.html).
|
||||
Also take a look on [this sample](https://github.com/typeorm/browser-example).
|
||||
More information about it is in [this page](https://typeorm.github.io/usage-in-browser.html).
|
||||
Also take a look at [this sample](https://github.com/typeorm/browser-example).
|
||||
|
||||
## Quick Start
|
||||
## Quick start
|
||||
|
||||
In TypeORM tables are created from Entities.
|
||||
*Entity* is your model decorated by a `@Entity` decorator.
|
||||
In TypeORM, tables are created from Entities.
|
||||
*Entity* is your model decorated by an `@Entity` decorator.
|
||||
You can get entities from the database and insert/update/remove them from there.
|
||||
Let's say we have a model `entity/Photo.ts`:
|
||||
|
||||
@ -142,7 +142,7 @@ export class Photo {
|
||||
}
|
||||
````
|
||||
|
||||
### Creating entity
|
||||
### Create an entity
|
||||
|
||||
Now let's make it entity:
|
||||
|
||||
@ -160,9 +160,9 @@ export class Photo {
|
||||
}
|
||||
```
|
||||
|
||||
### Add table columns
|
||||
### Adding table columns
|
||||
|
||||
Now we have a table, and each table consist of columns.
|
||||
Now we have a table, and each table consists of columns.
|
||||
Let's add some columns.
|
||||
You can make any property of your model a column by using a `@Column` decorator:
|
||||
|
||||
@ -192,10 +192,10 @@ export class Photo {
|
||||
}
|
||||
```
|
||||
|
||||
### Create a primary column
|
||||
### Creating a primary column
|
||||
|
||||
Perfect.
|
||||
Now ORM will generate us a photo table with all its properties as columns.
|
||||
Now the ORM will generate a photo table for us with all its properties as columns.
|
||||
But there is one thing left.
|
||||
Each entity must have a primary column.
|
||||
This is requirement and you can't avoid it.
|
||||
@ -227,9 +227,9 @@ export class Photo {
|
||||
}
|
||||
```
|
||||
|
||||
### Create auto-increment / generated / sequence / identity column
|
||||
### Creating an auto-increment / generated / sequence / identity column
|
||||
|
||||
Now, let's say you want to make your id column to be auto-generated (this is known as auto-increment / sequence / generated identity column).
|
||||
Now, let's say you want your id column to be auto-generated (this is known as auto-increment / sequence / generated identity column).
|
||||
To do that you need to change your column's type to integer and set a `{ generated: true }` in your primary column's options:
|
||||
|
||||
```typescript
|
||||
@ -258,10 +258,10 @@ export class Photo {
|
||||
}
|
||||
```
|
||||
|
||||
### Using `@PrimaryGeneratedColumn` decorator
|
||||
### Using the `@PrimaryGeneratedColumn` decorator
|
||||
|
||||
Now your photo's id will always be a generated, auto increment value.
|
||||
Since this is a common task - to create a generated auto increment primary column,
|
||||
Now your photo's id will always be a generated, auto-increment value.
|
||||
Since creating a generated auto-incrementing primary column is a common task,
|
||||
there is a special decorator called `@PrimaryGeneratedColumn` to do the same.
|
||||
Let's use it instead:
|
||||
|
||||
@ -293,8 +293,8 @@ export class Photo {
|
||||
|
||||
### Custom column data types
|
||||
|
||||
Next step, let's fix our data types. By default, string is mapped to a varchar(255)-like type (depend of database type).
|
||||
Number is mapped to a float/double-like type (depend of database type).
|
||||
Next, let's fix our data types. By default, string is mapped to a varchar(255)-like type (depending on the database type).
|
||||
Number is mapped to a float/double-like type (depending on the database type).
|
||||
We don't want all our columns to be limited varchars or excessive floats.
|
||||
Let's setup correct data types:
|
||||
|
||||
@ -326,9 +326,9 @@ export class Photo {
|
||||
}
|
||||
```
|
||||
|
||||
### Creating connection with the database
|
||||
### Creating a connection to the database
|
||||
|
||||
Now, when our entity is created, let's create `app.ts` file and setup our connection there:
|
||||
Now that our entity is created, let's create an `app.ts` file and set up our connection there:
|
||||
|
||||
```typescript
|
||||
import "reflect-metadata";
|
||||
@ -354,7 +354,7 @@ createConnection({
|
||||
```
|
||||
|
||||
We are using MySQL in this example, but you can use any other database.
|
||||
To use another database simply change type in the driver options to the database type you are using:
|
||||
To use another database, simply change the type in the driver options to the database type you are using:
|
||||
mysql, mariadb, postgres, sqlite, mssql or oracle.
|
||||
Also make sure to use your own host, port, username, password and database settings.
|
||||
|
||||
@ -366,8 +366,7 @@ Setting `autoSchemaSync` makes sure your entities will be synced with the databa
|
||||
### Loading all entities from the directory
|
||||
|
||||
Later, when we create more entities we need to add them to the entities in our configuration.
|
||||
But this is not very convenient, and instead we can setup the whole directory,
|
||||
where from all entities will be connected and used in our connection:
|
||||
This is not very convenient, so instead we can set up the whole directory, from where all entities will be connected and used in our connection:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -390,9 +389,9 @@ createConnection({
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
### Run the application
|
||||
### Running the application
|
||||
|
||||
Now you if run your `app.ts`, connection with database will be initialized and database table for your Photo will be created.
|
||||
Now if you run your `app.ts`, a connection with database will be initialized and a database table for your photos will be created.
|
||||
|
||||
|
||||
```shell
|
||||
@ -409,7 +408,7 @@ Now you if run your `app.ts`, connection with database will be initialized and d
|
||||
```
|
||||
|
||||
|
||||
### Creating and inserting photo into the database
|
||||
### Creating and inserting a photo into the database
|
||||
|
||||
Now let's create a new photo to save it in the database:
|
||||
|
||||
@ -436,7 +435,7 @@ createConnection(/*...*/).then(connection => {
|
||||
|
||||
### Using async/await syntax
|
||||
|
||||
Let's use latest TypeScript advantages and use async/await syntax instead:
|
||||
Let's take advantage of the latest TypeScript features and use async/await syntax instead:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -477,13 +476,13 @@ createConnection(/*...*/).then(async connection => {
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
savedPhotos will be an array of Photo objects with the data loaded from the database.
|
||||
`savedPhotos` will be an array of Photo objects with the data loaded from the database.
|
||||
|
||||
### Using Repositories
|
||||
|
||||
Now let's refactor our code and use `Repository` instead of EntityManager.
|
||||
Now let's refactor our code and use `Repository` instead of `EntityManager`.
|
||||
Each entity has its own repository which handles all operations with its entity.
|
||||
When you deal with entities a lot, Repositories are more convenient to use then EntityManager:
|
||||
When you deal with entities a lot, Repositories are more convenient to use than EntityManagers:
|
||||
|
||||
|
||||
```typescript
|
||||
@ -512,7 +511,7 @@ createConnection(/*...*/).then(async connection => {
|
||||
|
||||
### Loading photos from the database
|
||||
|
||||
Let's try more load operations using Repository:
|
||||
Let's try to more load operations using the Repository:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -543,7 +542,7 @@ createConnection(/*...*/).then(async connection => {
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
### Updating photo in the database
|
||||
### Updating a photo in the database
|
||||
|
||||
Now let's load a single photo from the database, update it and save it:
|
||||
|
||||
@ -563,7 +562,7 @@ createConnection(/*...*/).then(async connection => {
|
||||
|
||||
Now photo with `id = 1` will be updated in the database.
|
||||
|
||||
### Removing photo from the database
|
||||
### Removing a photo from the database
|
||||
|
||||
Now let's remove our photo from the database:
|
||||
|
||||
@ -585,7 +584,7 @@ Now photo with `id = 1` will be removed from the database.
|
||||
### Creating a one-to-one relation
|
||||
|
||||
Let's create a one-to-one relation with another class.
|
||||
Let's create a new class called `PhotoMetadata.ts` which will contain a PhotoMetadata class which supposed to contain our photo's additional meta-information:
|
||||
Let's create a new class in `PhotoMetadata.ts`. This PhotoMetadata class is supposed to contain our photo's additional meta-information:
|
||||
|
||||
```typescript
|
||||
import {Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
|
||||
@ -618,18 +617,18 @@ export class PhotoMetadata {
|
||||
}
|
||||
```
|
||||
|
||||
Here, we are used a new decorator called `@OneToOne`. It allows to create one-to-one relations between two entities.
|
||||
Here, we are used a new decorator called `@OneToOne`. It allows us to create one-to-one relations between two entities.
|
||||
`type => Photo` is a function that returns the class of the entity with which we want to make our relation.
|
||||
We are forced to use a function that returns a class, instead of using class directly, because of the language specifics.
|
||||
We can also write it as a `() => Photo`, but we use `type => Photo as convention to increase code readability. `
|
||||
Type variable itself does not contain anything.
|
||||
We can also write it as a `() => Photo`, but we use `type => Photo` as convention to increase code readability.
|
||||
The type variable itself does not contain anything.
|
||||
|
||||
We also put `@JoinColumn` decorator, which indicates that this side of the relationship will be owning relationship.
|
||||
Relations can be a unidirectional and bidirectional.
|
||||
We also add a `@JoinColumn` decorator, which indicates that this side of the relationship will own the relationship.
|
||||
Relations can be unidirectional or bidirectional.
|
||||
Only one side of relational can be owner.
|
||||
Using this decorator is required on owner side of the relationship.
|
||||
Using this decorator is required on the owner side of the relationship.
|
||||
|
||||
If you run the app you'll see a new generated table, and it will contain a column with a foreign key for the photo relation:
|
||||
If you run the app, you'll see a newly generated table, and it will contain a column with a foreign key for the photo relation:
|
||||
|
||||
```shell
|
||||
+-------------+--------------+----------------------------+
|
||||
@ -645,7 +644,7 @@ If you run the app you'll see a new generated table, and it will contain a colum
|
||||
+-------------+--------------+----------------------------+
|
||||
```
|
||||
|
||||
### Persisting an object with one-to-one relation
|
||||
### Persisting an object with a one-to-one relation
|
||||
|
||||
Now let's save a photo, its metadata and attach them to each other.
|
||||
|
||||
@ -688,13 +687,13 @@ createConnection(/*...*/).then(async connection => {
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
### Adding inverse side of a relation
|
||||
### Adding the inverse side of a relation
|
||||
|
||||
Relations can be a unidirectional and bidirectional.
|
||||
Now, relation between PhotoMetadata and Photo is unidirectional.
|
||||
Owner of the relation is PhotoMetadata and Photo doesn't know anything about PhotoMetadata.
|
||||
This makes complicated accessing a photo metadata from the photo objects.
|
||||
To fix it we should add inverse relation and make relations between PhotoMetadata and Photo bidirectional.
|
||||
Relations can be unidirectional or bidirectional.
|
||||
Currently, our relation between PhotoMetadata and Photo is unidirectional.
|
||||
The owner of the relation is PhotoMetadata, and Photo doesn't know anything about PhotoMetadata.
|
||||
This makes it complicated to access photo metadata from the photo objects.
|
||||
To fix it we should add an inverse relation, and make relations between PhotoMetadata and Photo bidirectional.
|
||||
Let's modify our entities:
|
||||
|
||||
```typescript
|
||||
@ -726,21 +725,21 @@ export class Photo {
|
||||
}
|
||||
```
|
||||
|
||||
`photo => photo.metadata` is a function that returns a name of the inverse side of the relation.
|
||||
Here we show that metadata property of the Photo class is where we store PhotoMetadata in the Photo class.
|
||||
You could also instead of passing function that returns a property of the photo simply pass a string to `@OneToOne` decorator, like `"metadata"`.
|
||||
But we used this function-typed approach to make your refactorings easier.
|
||||
`photo => photo.metadata` is a function that returns the name of the inverse side of the relation.
|
||||
Here we show that the metadata property of the Photo class is where we store PhotoMetadata in the Photo class.
|
||||
Instead of passing a function that returns a property of the photo, you could alternatively simply pass a string to `@OneToOne` decorator, like `"metadata"`.
|
||||
But we used this function-typed approach to make your refactoring easier.
|
||||
|
||||
Note that we should use `@JoinColumn` only on one side of relation.
|
||||
On which side you put this decorator, that side will be owning side of relationship.
|
||||
Owning side of relationship contain a column with a foreign key in the database.
|
||||
Note that we should use `@JoinColumn` on one side of a relation only.
|
||||
Whichever side you put this decorator on will be the owning side of the relationship.
|
||||
The owning side of a relationship contains a column with a foreign key in the database.
|
||||
|
||||
### Loading object with their relations
|
||||
### Loading objects with their relations
|
||||
|
||||
Now let's load our photo, and its photo metadata in a single query.
|
||||
There are two ways to do it - one you can use `FindOptions`, second is to use QueryBuilder.
|
||||
Let's use FindOptions first.
|
||||
`Repository.find` method allows you to specify object with FindOptions interface.
|
||||
There are two ways to do it -- either with `FindOptions` or `QueryBuilder`.
|
||||
Let's use `FindOptions` first.
|
||||
The `Repository.find` method allows you to specify an object with the `FindOptions` interface.
|
||||
Using this you can customize your query to perform more complex queries.
|
||||
|
||||
```typescript
|
||||
@ -763,16 +762,16 @@ createConnection(/*...*/).then(async connection => {
|
||||
}).catch(error => console.log(error));
|
||||
```
|
||||
|
||||
Here photos will contain array of photos from the database, and each photo will contain its photo metadata.
|
||||
Here photos will contain an array of photos from the database, and each photo will contain its photo metadata.
|
||||
|
||||
`alias` is a required property of FindOptions. Its your own alias name of the data you are selecting.
|
||||
You'll use this alias in your where, order by, group by, join and other expressions.
|
||||
`alias` is a required property of `FindOptions`. It's your own alias name of the data you are selecting.
|
||||
You'll use this alias in your `where`, `order by`, `group by`, `join` and other expressions.
|
||||
|
||||
We also used `innerJoinAndSelect` to inner and join and select the data from photo.metadata.
|
||||
In `"photo.metadata"` "photo" is an alias you used, and "metadata" is a property name with relation of the object you are selecting.
|
||||
`"metadata"`: is a new alias to the data returned by join expression.
|
||||
We also used `innerJoinAndSelect` to inner join and select the data from `photo.metadata`.
|
||||
In `photo.metadata`, `photo` is an alias you used, and `metadata` is a property name with relation of the object you are selecting.
|
||||
`"metadata"`: is a new alias to the data returned by the join expression.
|
||||
|
||||
Let's use `QueryBuilder` for the same purpose. QueryBuilder allows to use more complex queries in an elegant way:
|
||||
Let's use `QueryBuilder` for the same purpose. `QueryBuilder` allows us to use more complex queries in an elegant way:
|
||||
|
||||
```typescript
|
||||
import {createConnection} from "typeorm";
|
||||
@ -793,7 +792,7 @@ createConnection(/*...*/).then(async connection => {
|
||||
|
||||
### Using cascade options to automatically save related objects
|
||||
|
||||
We can setup cascade options in our relations, in the cases when we want our related object to be persisted whenever other object is saved.
|
||||
We can setup cascade options in our relations, in the cases when we want our related object to be persisted whenever the other object is saved.
|
||||
Let's change our photo's `@OneToOne` decorator a bit:
|
||||
|
||||
```typescript
|
||||
@ -810,12 +809,12 @@ export class Photo {
|
||||
```
|
||||
|
||||
* **cascadeInsert** - automatically insert metadata in the relation if it does not exist in its table.
|
||||
This means that we don't need to manually insert a newly created photoMetadata object.
|
||||
* **cascadeUpdate** - automatically update metadata in the relation if in this object something is changed.
|
||||
This means that we don't need to manually insert a newly created `photoMetadata` object.
|
||||
* **cascadeUpdate** - automatically update metadata in the relation if something is changed in this object.
|
||||
* **cascadeRemove** - automatically remove metadata from its table if you removed metadata from photo object.
|
||||
|
||||
Using cascadeInsert allows us not to separately persist photo and separately persist metadata objects now.
|
||||
Now we can simply persist a photo object, and metadata object will persist automatically because of cascade options.
|
||||
Using `cascadeInsert` allows us not to separately persist photo and separately persist metadata objects now.
|
||||
Now we can simply persist a photo object, and the metadata object will persist automatically because of cascade options.
|
||||
|
||||
```typescript
|
||||
createConnection(options).then(async connection => {
|
||||
@ -852,7 +851,7 @@ createConnection(options).then(async connection => {
|
||||
|
||||
Let's create a many-to-one / one-to-many relation.
|
||||
Let's say a photo has one author, and each author can have many photos.
|
||||
First, let's create Author class:
|
||||
First, let's create an `Author` class:
|
||||
|
||||
```typescript
|
||||
import {Entity, Column, PrimaryGeneratedColumn, OneToMany, JoinColumn} from "typeorm";
|
||||
@ -872,10 +871,10 @@ export class Author {
|
||||
}
|
||||
```
|
||||
|
||||
Author contains an inverse side of a relationship.
|
||||
OneToMany is always an inverse side of relation, and it can't exist without ManyToOne of the other side of relationship.
|
||||
`Author` contains an inverse side of a relation.
|
||||
`OneToMany` is always an inverse side of relation, and it can't exist without `ManyToOne` on the other side of the relation.
|
||||
|
||||
Now let's add owner side of relationship into the Photo entity:
|
||||
Now let's add the owner side of the relation into the Photo entity:
|
||||
|
||||
```typescript
|
||||
import {Entity, Column, PrimaryGeneratedColumn, ManyToOne} from "typeorm";
|
||||
@ -892,10 +891,10 @@ export class Photo {
|
||||
}
|
||||
```
|
||||
|
||||
In many-to-one / one-to-many relation, owner side is always many-to-one.
|
||||
It means that class which uses `@ManyToOne` will store id of the related object.
|
||||
In many-to-one / one-to-many relation, the owner side is always many-to-one.
|
||||
It means that the class that uses `@ManyToOne` will store the id of the related object.
|
||||
|
||||
After you run application ORM will create author table:
|
||||
After you run the application, the ORM will create the `author` table:
|
||||
|
||||
|
||||
```shell
|
||||
@ -907,7 +906,7 @@ After you run application ORM will create author table:
|
||||
+-------------+--------------+----------------------------+
|
||||
```
|
||||
|
||||
It will also modify photo table - add a new column author and create a foreign key for it:
|
||||
It will also modify the `photo` table, adding a new `author` column and creating a foreign key for it:
|
||||
|
||||
```shell
|
||||
+-------------+--------------+----------------------------+
|
||||
@ -925,7 +924,7 @@ It will also modify photo table - add a new column author and create a foreign k
|
||||
### Creating a many-to-many relation
|
||||
|
||||
Let's create a many-to-one / many-to-many relation.
|
||||
Let's say a photo can be in many albums, and multiple can have many photos.
|
||||
Let's say a photo can be in many albums, and each album can contain many photos.
|
||||
Let's create an `Album` class:
|
||||
|
||||
```typescript
|
||||
@ -950,9 +949,9 @@ export class Album {
|
||||
}
|
||||
```
|
||||
|
||||
`@JoinTable` is required to specify that this is owner side of the relationship.
|
||||
`@JoinTable` is required to specify that this is the owner side of the relationship.
|
||||
|
||||
Now let's add inverse side of our relation to the `Photo` class:
|
||||
Now let's add the inverse side of our relation to the `Photo` class:
|
||||
|
||||
```typescript
|
||||
export class Photo {
|
||||
@ -967,7 +966,7 @@ export class Photo {
|
||||
}
|
||||
```
|
||||
|
||||
After you run application ORM will create a **album_photos_photo_albums** *junction table*:
|
||||
After you run thr application, the ORM will create a **album_photos_photo_albums** *junction table*:
|
||||
|
||||
```shell
|
||||
+-------------+--------------+----------------------------+
|
||||
@ -978,7 +977,7 @@ After you run application ORM will create a **album_photos_photo_albums** *junct
|
||||
+-------------+--------------+----------------------------+
|
||||
```
|
||||
|
||||
Don't forget to register `Album` class for your connection in the ORM:
|
||||
Don't forget to register the `Album` class with your connection in the ORM:
|
||||
|
||||
```typescript
|
||||
const options: CreateConnectionOptions = {
|
||||
@ -1045,13 +1044,13 @@ let photos = await photoRepository
|
||||
.getMany();
|
||||
```
|
||||
|
||||
This query builder will select you all photos that are published and whose name is "My" or "Mishka",
|
||||
it will select results from 5 position (pagination offset),
|
||||
This query builder will select all photos that are published and whose name is "My" or "Mishka".
|
||||
It will select results from position 5 (pagination offset),
|
||||
and will select only 10 results (pagination limit).
|
||||
Selection result will be ordered by id in descending order.
|
||||
Photo's albums will be left-joined and photo's metadata will be inner joined.
|
||||
The selection result will be ordered by id in descending order.
|
||||
The photos' albums will be left-joined and their metadata will be inner joined.
|
||||
|
||||
You'll use query builder in your application a lot. Learn more about QueryBuilder [here](https://typeorm.github.io/query-builder.html).
|
||||
You'll use the query builder in your application a lot. Learn more about QueryBuilder [here](https://typeorm.github.io/query-builder.html).
|
||||
|
||||
## Learn more
|
||||
|
||||
@ -1074,9 +1073,9 @@ You'll use query builder in your application a lot. Learn more about QueryBuilde
|
||||
|
||||
## Samples
|
||||
|
||||
Take a look on samples in [./sample](sample) for examples of usage.
|
||||
Take a look at the samples in [./sample](sample) for examples of usage.
|
||||
|
||||
There are few repositories which you can clone and start with:
|
||||
There are a few repositories which you can clone and start with:
|
||||
|
||||
* [Example how to use TypeORM with TypeScript](https://github.com/typeorm/typescript-example)
|
||||
* [Example how to use TypeORM with JavaScript](https://github.com/typeorm/javascript-example)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user