[frontend] Add openapi generator and annotate OpenAPI in backend

This commit is contained in:
Cathy Hu 2020-07-25 13:38:10 +02:00
parent cb9e8e0136
commit 87a315f340
11 changed files with 268 additions and 60 deletions

View file

@ -1623,6 +1623,11 @@
}
}
},
"@openapitools/openapi-generator-cli": {
"version": "1.0.15-4.3.1",
"resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-1.0.15-4.3.1.tgz",
"integrity": "sha512-U+sanspDmeBElVNjYHQ4U7BbSEJUQzjNKmiTzXpcEw/r93sgxmzS2Sew5t+Zj6kyN1YTvjhRjJikNcW9/bmTKA=="
},
"@schematics/angular": {
"version": "9.1.12",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-9.1.12.tgz",

View file

@ -19,6 +19,7 @@
"@angular/platform-browser": "~9.1.4",
"@angular/platform-browser-dynamic": "~9.1.4",
"@angular/router": "~9.1.4",
"@openapitools/openapi-generator-cli": "^1.0.15-4.3.1",
"rxjs": "~6.5.4",
"tslib": "^1.10.0",
"zone.js": "~0.10.2"

View file

@ -5,14 +5,9 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent]
bootstrap: [AppComponent],
})
export class AppModule {}

View file

@ -3,7 +3,8 @@
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
production: false,
backendUrl: 'http://localhost:8000/api',
};
/*

View file

@ -70,12 +70,21 @@ async def get_subscriber(subscriber_id: int, hood=Depends(get_hood)):
router = APIRouter()
@router.get('/')
@router.get(
'/',
# TODO response_model
operation_id='get_all',
)
async def email_read_all(hood=Depends(get_hood)):
return await Email.objects.filter(hood=hood).all()
@router.post('/', status_code=status.HTTP_201_CREATED)
@router.post(
'/',
status_code=status.HTTP_201_CREATED,
# TODO response_model
operation_id='create',
)
async def email_create(values: BodyEmail, response: Response, hood=Depends(get_hood)):
""" Create an Email bot. Call this when creating a hood.
@ -92,31 +101,52 @@ async def email_create(values: BodyEmail, response: Response, hood=Depends(get_h
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
@router.get('/status', status_code=status.HTTP_200_OK)
@router.get(
'/status',
status_code=status.HTTP_200_OK,
# TODO response_model
operation_id='status',
)
async def email_status(hood=Depends(get_hood)):
return {'status': spawner.get(hood).status.name}
@router.post('/start', status_code=status.HTTP_200_OK)
@router.post(
'/start',
status_code=status.HTTP_200_OK,
# TODO response_model
operation_id='start',
)
async def email_start(hood=Depends(get_hood)):
await hood.update(email_enabled=True)
spawner.get(hood).start()
return {}
@router.post('/stop', status_code=status.HTTP_200_OK)
@router.post(
'/stop',
status_code=status.HTTP_200_OK,
# TODO response_model
operation_id='stop',
)
async def email_stop(hood=Depends(get_hood)):
await hood.update(email_enabled=False)
spawner.get(hood).stop()
return {}
@router.get('/{email_id}')
@router.get(
'/{email_id}',
# TODO response_model
operation_id='get',
)
async def email_read(email=Depends(get_email)):
return email
@router.delete('/{email_id}', status_code=status.HTTP_204_NO_CONTENT)
@router.delete(
'/{email_id}', status_code=status.HTTP_204_NO_CONTENT, operation_id='delete'
)
async def email_delete(email=Depends(get_email)):
""" Delete an Email bot.
Stops and deletes the Email bot.
@ -126,7 +156,12 @@ async def email_delete(email=Depends(get_email)):
await email.delete()
@router.post('/subscribe/', status_code=status.HTTP_202_ACCEPTED)
@router.post(
'/subscribe/',
status_code=status.HTTP_202_ACCEPTED,
operation_id='subscribe',
response_model=BaseModel,
)
async def email_subscribe(
subscriber: BodySubscriber, hood=Depends(get_hood_unauthorized)
):
@ -160,7 +195,12 @@ async def email_subscribe(
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY)
@router.post('/subscribe/confirm/{token}', status_code=status.HTTP_201_CREATED)
@router.post(
'/subscribe/confirm/{token}',
status_code=status.HTTP_201_CREATED,
operation_id='confirm_subscriber',
response_model=BaseModel,
)
async def email_subscribe_confirm(token, hood=Depends(get_hood_unauthorized)):
""" Confirm a new subscriber and add them to the database.
@ -179,7 +219,11 @@ async def email_subscribe_confirm(token, hood=Depends(get_hood_unauthorized)):
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
@router.delete('/unsubscribe/{token}', status_code=status.HTTP_204_NO_CONTENT)
@router.delete(
'/unsubscribe/{token}',
status_code=status.HTTP_204_NO_CONTENT,
operation_id='unsubscribe',
)
async def email_unsubscribe(token, hood=Depends(get_hood_unauthorized)):
""" Remove a subscriber from the database when they click on an unsubscribe link.
@ -197,17 +241,30 @@ async def email_unsubscribe(token, hood=Depends(get_hood_unauthorized)):
await subscriber.delete()
@router.get('/subscribers/')
@router.get(
'/subscribers/',
# TODO response_model
operation_id='get_subscribers',
)
async def subscribers_read_all(hood=Depends(get_hood)):
return await EmailSubscribers.objects.filter(hood=hood).all()
@router.get('/subscribers/{subscriber_id}')
@router.get(
'/subscribers/{subscriber_id}',
# TODO response_model
operation_id='get_subscribers',
)
async def subscribers_read(subscriber=Depends(get_subscriber)):
return subscriber
@router.post('/messages/', status_code=status.HTTP_201_CREATED)
@router.post(
'/messages/',
status_code=status.HTTP_201_CREATED,
# TODO response_model
operation_id='create_email',
)
async def email_message_create(
message: BodyMessage, hood=Depends(get_hood_unauthorized)
):

View file

@ -41,23 +41,38 @@ router = APIRouter()
telegram_callback_router = APIRouter()
@router.get('/')
@router.get(
'/',
# TODO response_model,
operation_id='get_all',
)
async def telegram_read_all(hood=Depends(get_hood)):
return await Telegram.objects.filter(hood=hood).all()
@router.get('/{telegram_id}')
@router.get(
'/{telegram_id}',
# TODO response_model,
operation_id='get',
)
async def telegram_read(telegram=Depends(get_telegram)):
return telegram
@router.delete('/{telegram_id}', status_code=status.HTTP_204_NO_CONTENT)
@router.delete(
'/{telegram_id}', status_code=status.HTTP_204_NO_CONTENT, operation_id='delete'
)
async def telegram_delete(telegram=Depends(get_telegram)):
spawner.stop(telegram)
await telegram.delete()
@router.post('/', status_code=status.HTTP_201_CREATED)
@router.post(
'/',
status_code=status.HTTP_201_CREATED,
# TODO response_model,
operation_id='create',
)
async def telegram_create(
response: Response, values: BodyTelegram, hood=Depends(get_hood)
):
@ -70,7 +85,12 @@ async def telegram_create(
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
@router.put('/{telegram_id}', status_code=status.HTTP_202_ACCEPTED)
@router.put(
'/{telegram_id}',
status_code=status.HTTP_202_ACCEPTED,
# TODO response_model,
operation_id='update',
)
async def telegram_update(values: BodyTelegram, telegram=Depends(get_telegram)):
try:
spawner.stop(telegram)
@ -81,19 +101,34 @@ async def telegram_update(values: BodyTelegram, telegram=Depends(get_telegram)):
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
@router.get('/{telegram_id}/status', status_code=status.HTTP_200_OK)
@router.get(
'/{telegram_id}/status',
status_code=status.HTTP_200_OK,
# TODO response_model,
operation_id='status',
)
async def telegram_status(telegram=Depends(get_telegram)):
return {'status': spawner.get(telegram).status.name}
@router.post('/{telegram_id}/start', status_code=status.HTTP_200_OK)
@router.post(
'/{telegram_id}/start',
status_code=status.HTTP_200_OK,
# TODO response_model,
operation_id='start',
)
async def telegram_start(telegram=Depends(get_telegram)):
await telegram.update(enabled=True)
spawner.get(telegram).start()
return {}
@router.post('/{telegram_id}/stop', status_code=status.HTTP_200_OK)
@router.post(
'/{telegram_id}/stop',
status_code=status.HTTP_200_OK,
# TODO response_model,
operation_id='stop',
)
async def telegram_stop(telegram=Depends(get_telegram)):
await telegram.update(enabled=False)
spawner.get(telegram).stop()

View file

@ -28,42 +28,75 @@ router = APIRouter()
twitter_callback_router = APIRouter()
@router.get('/')
@router.get(
'/',
# TODO response_model,
operation_id='get_all',
)
async def twitter_read_all(hood=Depends(get_hood)):
return await Twitter.objects.filter(hood=hood).all()
@router.get('/{twitter_id}')
@router.get(
'/{twitter_id}',
# TODO response_model
operation_id='get',
)
async def twitter_read(twitter=Depends(get_twitter)):
return twitter
@router.delete('/{twitter_id}', status_code=status.HTTP_204_NO_CONTENT)
@router.delete(
'/{twitter_id}',
status_code=status.HTTP_204_NO_CONTENT,
# TODO response_model
operation_id='delete',
)
async def twitter_delete(twitter=Depends(get_twitter)):
spawner.stop(twitter)
await twitter.delete()
@router.get('/{twitter_id}/status', status_code=status.HTTP_200_OK)
@router.get(
'/{twitter_id}/status',
status_code=status.HTTP_200_OK,
# TODO response_model
operation_id='status',
)
async def twitter_status(twitter=Depends(get_twitter)):
return {'status': spawner.get(twitter).status.name}
@router.post('/{twitter_id}/start', status_code=status.HTTP_200_OK)
@router.post(
'/{twitter_id}/start',
status_code=status.HTTP_200_OK,
# TODO response_model
operation_id='start',
)
async def twitter_start(twitter=Depends(get_twitter)):
await twitter.update(enabled=True)
spawner.get(twitter).start()
return {}
@router.post('/{twitter_id}/stop', status_code=status.HTTP_200_OK)
@router.post(
'/{twitter_id}/stop',
status_code=status.HTTP_200_OK,
# TODO response_model
operation_id='stop',
)
async def twitter_stop(twitter=Depends(get_twitter)):
await twitter.update(enabled=False)
spawner.get(twitter).stop()
return {}
@router.post('/', status_code=status.HTTP_201_CREATED)
@router.post(
'/',
status_code=status.HTTP_201_CREATED,
# TODO response_model
operation_id='create',
)
async def twitter_create(response: Response, hood=Depends(get_hood)):
"""
`https://api.twitter.com/oauth/authorize?oauth_token=`
@ -89,7 +122,11 @@ async def twitter_create(response: Response, hood=Depends(get_hood)):
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
@twitter_callback_router.get('/callback')
@twitter_callback_router.get(
'/callback',
# TODO response_model
operation_id='callback',
)
async def twitter_read_callback(oauth_token: str, oauth_verifier: str):
try:
twitter = await Twitter.objects.filter(access_token=oauth_token).get()

View file

@ -31,6 +31,11 @@ class BodyAdmin(BaseModel):
password: str
class BodyAccessToken(BaseModel):
access_token: str
token_type: str = 'bearer'
oauth2_scheme = OAuth2PasswordBearer(tokenUrl='/api/admin/login')
secret_box = SecretBox(random(SecretBox.KEY_SIZE))
@ -72,7 +77,12 @@ async def get_admin(access_token=Depends(oauth2_scheme)):
router = APIRouter()
@router.post('/register/', status_code=status.HTTP_202_ACCEPTED)
@router.post(
'/register/',
status_code=status.HTTP_202_ACCEPTED,
response_model=BaseModel,
operation_id='register',
)
async def admin_register(values: BodyAdmin):
""" Sends an email with a confirmation link.
@ -100,7 +110,9 @@ async def admin_register(values: BodyAdmin):
return {}
@router.post('/confirm/{register_token}')
@router.post(
'/confirm/{register_token}', response_model=BodyAccessToken, operation_id='confirm',
)
async def admin_confirm(register_token: str):
""" Registration confirmation and account creation.
@ -110,12 +122,14 @@ async def admin_confirm(register_token: str):
values = from_token(register_token)
passhash = argon2.hash(values['password'])
await Admin.objects.create(email=values['email'], passhash=passhash)
return {'access_token': register_token, 'token_type': 'bearer'}
return BodyAccessToken(access_token=register_token)
except IntegrityError:
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
@router.post('/login/')
@router.post(
'/login/', response_model=BodyAccessToken, operation_id='login',
)
async def admin_login(form_data: OAuth2PasswordRequestForm = Depends()):
""" Get an access token.
@ -130,10 +144,14 @@ async def admin_login(form_data: OAuth2PasswordRequestForm = Depends()):
detail='Incorrect email or password',
)
token = to_token(email=form_data.username, password=form_data.password)
return {'access_token': token, 'token_type': 'bearer'}
return BodyAccessToken(access_token=token)
@router.get('/hoods/')
@router.get(
'/hoods/',
# TODO response_model,
operation_id='get_hoods',
)
async def admin_hood_read_all(admin=Depends(get_admin)):
""" Get a list of all hoods of a given admin. """
return (

View file

@ -43,13 +43,22 @@ async def get_hood(hood=Depends(get_hood_unauthorized), admin=Depends(get_admin)
router = APIRouter()
@router.get('/')
@router.get(
'/',
# TODO response_model,
operation_id='get_all',
)
async def hood_read_all():
""" Get all existing hoods. """
return await Hood.objects.all()
@router.post('/', status_code=status.HTTP_201_CREATED)
@router.post(
'/',
status_code=status.HTTP_201_CREATED,
# TODO response_model,
operation_id='create_hood',
)
async def hood_create(values: BodyHood, response: Response, admin=Depends(get_admin)):
""" Creates a hood.
@ -66,13 +75,19 @@ async def hood_create(values: BodyHood, response: Response, admin=Depends(get_ad
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
@router.get('/{hood_id}')
@router.get(
'/{hood_id}',
# TODO response_model,
operation_id='get_hood',
)
async def hood_read(hood=Depends(get_hood)):
""" Get hood with id **hood_id**. """
return hood
@router.put('/{hood_id}', status_code=status.HTTP_204_NO_CONTENT)
@router.put(
'/{hood_id}', status_code=status.HTTP_204_NO_CONTENT, operation_id='update_hood',
)
async def hood_update(values: BodyHood, hood=Depends(get_hood)):
""" Updates hood with id **hood_id**.
@ -82,7 +97,9 @@ async def hood_update(values: BodyHood, hood=Depends(get_hood)):
await hood.update(**values.__dict__)
@router.delete('/{hood_id}', status_code=status.HTTP_204_NO_CONTENT)
@router.delete(
'/{hood_id}', status_code=status.HTTP_204_NO_CONTENT, operation_id='update_hood',
)
async def hood_delete(hood=Depends(get_hood)):
""" Deletes hood with id **hood_id**. """
for relation in await AdminHoodRelation.objects.filter(hood=hood).all():

View file

@ -33,13 +33,22 @@ async def get_badword(badword_id: int, hood=Depends(get_hood)):
router = APIRouter()
@router.get('/')
@router.get(
'/',
# TODO response_model,
operation_id='get_badwords',
)
async def badword_read_all(hood=Depends(get_hood)):
""" Get all badwords of hood with id **hood_id**. """
return await BadWord.objects.filter(hood=hood).all()
@router.post('/', status_code=status.HTTP_201_CREATED)
@router.post(
'/',
status_code=status.HTTP_201_CREATED,
# TODO response_model,
operation_id='create_badword',
)
async def badword_create(
values: BodyBadWord, response: Response, hood=Depends(get_hood)
):
@ -58,13 +67,21 @@ async def badword_create(
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)
@router.get('/{badword_id}')
@router.get(
'/{badword_id}',
# TODO response_model,
operation_id='get_badword',
)
async def badword_read(badword=Depends(get_badword)):
""" Reads badword with id **badword_id** for hood with id **hood_id**. """
return badword
@router.put('/{badword_id}', status_code=status.HTTP_204_NO_CONTENT)
@router.put(
'/{badword_id}',
status_code=status.HTTP_204_NO_CONTENT,
operation_id='update_badword',
)
async def badword_update(values: BodyBadWord, badword=Depends(get_badword)):
""" Updates badword with id **badword_id** for hood with id **hood_id**.
@ -73,7 +90,11 @@ async def badword_update(values: BodyBadWord, badword=Depends(get_badword)):
await badword.update(**values.__dict__)
@router.delete('/{badword_id}', status_code=status.HTTP_204_NO_CONTENT)
@router.delete(
'/{badword_id}',
status_code=status.HTTP_204_NO_CONTENT,
operation_id='delete_badword',
)
async def badword_delete(badword=Depends(get_badword)):
""" Deletes badword with id **badword_id** for hood with id **hood_id**. """
await badword.delete()

View file

@ -34,13 +34,22 @@ async def get_trigger(trigger_id: int, hood=Depends(get_hood)):
router = APIRouter()
@router.get('/')
@router.get(
'/',
# TODO response_model,
operation_id='get_triggers',
)
async def trigger_read_all(hood=Depends(get_hood)):
""" Get all triggers of hood with id **hood_id**. """
return await Trigger.objects.filter(hood=hood).all()
@router.post('/', status_code=status.HTTP_201_CREATED)
@router.post(
'/',
status_code=status.HTTP_201_CREATED,
# TODO response_model,
operation_id='create_trigger',
)
async def trigger_create(
values: BodyTrigger, response: Response, hood=Depends(get_hood)
):
@ -59,13 +68,21 @@ async def trigger_create(
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)
@router.get('/{trigger_id}')
@router.get(
'/{trigger_id}',
# TODO response_model,
operation_id='get_trigger',
)
async def trigger_read(trigger=Depends(get_trigger)):
""" Reads trigger with id **trigger_id** for hood with id **hood_id**. """
return trigger
@router.put('/{trigger_id}', status_code=status.HTTP_204_NO_CONTENT)
@router.put(
'/{trigger_id}',
status_code=status.HTTP_204_NO_CONTENT,
operation_id='update_trigger',
)
async def trigger_update(values: BodyTrigger, trigger=Depends(get_trigger)):
""" Updates trigger with id **trigger_id** for hood with id **hood_id**.
@ -74,7 +91,11 @@ async def trigger_update(values: BodyTrigger, trigger=Depends(get_trigger)):
await trigger.update(**values.__dict__)
@router.delete('/{trigger_id}', status_code=status.HTTP_204_NO_CONTENT)
@router.delete(
'/{trigger_id}',
status_code=status.HTTP_204_NO_CONTENT,
operation_id='delete_trigger',
)
async def trigger_delete(trigger=Depends(get_trigger)):
""" Deletes trigger with id **trigger_id** for hood with id **hood_id**. """
await trigger.delete()