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(cli): Generate full client test suite and improve typed client #2788

Merged
merged 7 commits into from
Oct 12, 2022
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
38,088 changes: 27,156 additions & 10,932 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/authentication/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"access": "public"
},
"dependencies": {
"@feathersjs/hooks": "^0.7.5",
"@feathersjs/commons": "^5.0.0-pre.30",
"@feathersjs/errors": "^5.0.0-pre.30",
"@feathersjs/feathers": "^5.0.0-pre.30",
Expand Down
24 changes: 13 additions & 11 deletions packages/authentication/src/service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import merge from 'lodash/merge'
import { NotAuthenticated } from '@feathersjs/errors'
import { AuthenticationBase, AuthenticationResult, AuthenticationRequest, AuthenticationParams } from './core'
import { connection, event } from './hooks'
import '@feathersjs/transport-commons'
import { createDebug } from '@feathersjs/commons'
import { ServiceMethods, ServiceAddons } from '@feathersjs/feathers'
import { resolveDispatch } from '@feathersjs/schema'
import jsonwebtoken from 'jsonwebtoken'
import { hooks } from '@feathersjs/hooks'

import { AuthenticationBase, AuthenticationResult, AuthenticationRequest, AuthenticationParams } from './core'
import { connection, event } from './hooks'

const debug = createDebug('@feathersjs/authentication/service')

Expand Down Expand Up @@ -39,6 +41,15 @@ export class AuthenticationService
constructor(app: any, configKey = 'authentication', options = {}) {
super(app, configKey, options)

hooks(this, {
create: [resolveDispatch(), connection('login'), event('login')],
remove: [resolveDispatch(), connection('logout'), event('logout')]
})

this.app.on('disconnect', async (connection) => {
await this.handleConnection('disconnect', connection)
})

if (typeof app.defaultAuthentication !== 'function') {
app.defaultAuthentication = function (location?: string) {
const configKey = app.get('defaultAuthentication')
Expand Down Expand Up @@ -185,15 +196,6 @@ export class AuthenticationService
}
}

this.hooks({
create: [resolveDispatch(), connection('login'), event('login')],
remove: [resolveDispatch(), connection('logout'), event('logout')]
} as any)

this.app.on('disconnect', async (connection) => {
await this.handleConnection('disconnect', connection)
})

if (typeof this.publish === 'function') {
this.publish(() => null)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/authentication/test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ export class Strategy1 extends AuthenticationBaseStrategy {

async authenticate(authentication: AuthenticationRequest) {
if (authentication.username === 'David' || authentication.both) {
return Strategy1.result
return { ...Strategy1.result }
}

throw new NotAuthenticated('Invalid Dave')
}

async parse(req: MockRequest) {
if (req.isDave) {
return Strategy1.result
return { ...Strategy1.result }
}

return null
Expand Down
29 changes: 15 additions & 14 deletions packages/authentication/test/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,20 +212,21 @@ describe('authentication/service', () => {
assert.deepStrictEqual(authResult, Strategy1.result)
})

it('passes when id is set and does not match accessToken', async () => {
try {
await app.service('authentication').remove('test', {
authentication: {
strategy: 'first',
username: 'David',
accessToken: 'testing'
}
})
assert.fail('Should never get here')
} catch (error: any) {
assert.strictEqual(error.name, 'NotAuthenticated')
assert.strictEqual(error.message, 'Invalid access token')
}
it('fails when id is set and does not match accessToken', async () => {
await assert.rejects(
() =>
app.service('authentication').remove('test', {
authentication: {
strategy: 'first',
username: 'David',
accessToken: 'testing'
}
}),
{
name: 'NotAuthenticated',
message: 'Invalid access token'
}
)
})

it('errors when trying to remove with nothing', async () => {
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@
"@feathersjs/authentication": "^5.0.0-pre.30",
"@feathersjs/authentication-local": "^5.0.0-pre.30",
"@feathersjs/authentication-oauth": "^5.0.0-pre.30",
"@feathersjs/authentication-client": "^5.0.0-pre.30",
"@feathersjs/configuration": "^5.0.0-pre.30",
"@feathersjs/errors": "^5.0.0-pre.30",
"@feathersjs/express": "^5.0.0-pre.30",
"@feathersjs/feathers": "^5.0.0-pre.30",
"@feathersjs/knex": "^5.0.0-pre.30",
"@feathersjs/koa": "^5.0.0-pre.30",
"@feathersjs/rest-client": "^5.0.0-pre.30",
"@feathersjs/mongodb": "^5.0.0-pre.30",
"@feathersjs/schema": "^5.0.0-pre.30",
"@feathersjs/socketio": "^5.0.0-pre.30",
Expand Down
11 changes: 10 additions & 1 deletion packages/cli/src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,16 @@ export const generate = (ctx: AppGeneratorArguments) =>
.then(
install<AppGeneratorContext>(
({ language, framework, devDependencies, dependencyVersions }) => {
devDependencies.push('nodemon', 'axios', 'mocha', 'cross-env', 'prettier', '@feathersjs/cli')
devDependencies.push(
'nodemon',
'axios',
'mocha',
'cross-env',
'prettier',
'@feathersjs/cli',
'@feathersjs/rest-client',
'@feathersjs/authentication-client'
)

if (language === 'ts') {
devDependencies.push(
Expand Down
26 changes: 26 additions & 0 deletions packages/cli/src/app/templates/client.test.tpl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { generator, toFile } from '@feathershq/pinion'
import { renderSource } from '../../commons'
import { AppGeneratorContext } from '../index'

const template = ({ lib }: AppGeneratorContext) => /* ts */ `import assert from 'assert'
import axios from 'axios'
import type { Server } from 'http'
import { app } from '../${lib}/app'
import { createClient } from '../${lib}/client'

import rest from '@feathersjs/rest-client'

const port = app.get('port')
const appUrl = \`http://\${app.get('host')}:\${port}\`

describe('client tests', () => {
const client = createClient(rest(appUrl).axios(axios))

it('initialized the client', () => {
assert.ok(client)
})
})
`

export const generate = (ctx: AppGeneratorContext) =>
generator(ctx).then(renderSource(template, toFile('test', 'client.test')))
2 changes: 1 addition & 1 deletion packages/cli/src/app/templates/client.tpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { renderSource } from '../../commons'
import { AppGeneratorContext } from '../index'

const template = ({}: AppGeneratorContext) => /* ts */ `import { feathers } from '@feathersjs/feathers'
import type { Paginated, ClientService, TransportConnection, Params } from '@feathersjs/feathers'
import type { TransportConnection, Params } from '@feathersjs/feathers'

export interface ServiceTypes {
//
Expand Down
80 changes: 80 additions & 0 deletions packages/cli/src/authentication/templates/client.test.tpl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { generator, toFile } from '@feathershq/pinion'
import { renderSource } from '../../commons'
import { AuthenticationGeneratorContext } from '../index'

const template = ({
authStrategies,
upperName,
type,
lib
}: AuthenticationGeneratorContext) => /* ts */ `import assert from 'assert'
import axios from 'axios'

import rest from '@feathersjs/rest-client'
${
authStrategies.includes('local')
? `import authenticationClient from '@feathersjs/authentication-client'`
: ''
}
import { app } from '../${lib}/app'
import { createClient } from '../${lib}/client'
${authStrategies.includes('local') ? `import type { ${upperName}Data } from '../${lib}/client'` : ''}

const port = app.get('port')
const appUrl = \`http://\${app.get('host')}:\${port}\`

describe('application client tests', () => {
const client = createClient(rest(appUrl).axios(axios))

client.configure(authenticationClient())

before(async () => {
await app.listen(port)
})

after(async () => {
await app.teardown()
})

it('initialized the client', () => {
assert.ok(client)
})

${
authStrategies.includes('local')
? `
it('creates and authenticates a user with email and password', async () => {
const userData: ${upperName}Data = {
email: 'someone@example.com',
password: 'supersecret'
}

await client.service('users').create(userData)

const { user, accessToken } = await client.authenticate({
strategy: 'local',
...userData
})

assert.ok(accessToken, 'Created access token for user')
assert.ok(user, 'Includes user in authentication data')
assert.strictEqual(user.password, undefined, 'Password is hidden to clients')

await client.logout()

// Remove the test user on the server
await app.service('users').remove(user.${type === 'mongodb' ? '_id' : 'id'})
})`
: ''
}
})
`

export const generate = (ctx: AuthenticationGeneratorContext) =>
generator(ctx).then(
renderSource(
template,
toFile<AuthenticationGeneratorContext>(({ test }) => test, 'client.test'),
{ force: true }
)
)
19 changes: 19 additions & 0 deletions packages/cli/src/authentication/templates/client.tpl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { generator, toFile, after, when } from '@feathershq/pinion'
import { injectSource } from '../../commons'
import { ServiceGeneratorContext } from '../../service'

const importTemplate = /* ts */ `import type { AuthenticationService } from '@feathersjs/authentication'
`
const declarationTemplate = ` authentication: Pick<AuthenticationService, 'create' | 'remove'>`

const toClientFile = toFile<ServiceGeneratorContext>(({ lib }) => [lib, 'client'])

export const generate = async (ctx: ServiceGeneratorContext) =>
generator(ctx)
.then(injectSource(importTemplate, after("from '@feathersjs/feathers'"), toClientFile))
.then(
when(
({ language }) => language === 'ts',
injectSource(declarationTemplate, after('export interface ServiceTypes'), toClientFile)
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const importTemplate = ({
fileName
}: AuthenticationGeneratorContext) => /* ts */ `import { ${upperName} } from './services/${folder.join(
'/'
)}/${fileName}.schema'
)}/${fileName}'
`

const paramsTemplate = ({
Expand Down
53 changes: 0 additions & 53 deletions packages/cli/src/authentication/templates/test.tpl.ts

This file was deleted.

Loading