Skip to content

Commit

Permalink
update response when syncing engine (#326)
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel authored Jun 11, 2024
1 parent 722654a commit 2fd7fca
Show file tree
Hide file tree
Showing 16 changed files with 121 additions and 50 deletions.
25 changes: 11 additions & 14 deletions apps/armory/src/managed-data-store/__test__/e2e/data-store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ describe('Data Store', () => {

clusterService = mock<ClusterService>()

clusterService.sync.mockResolvedValue(true)

module = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
Expand Down Expand Up @@ -209,11 +211,11 @@ describe('Data Store', () => {
.send({ policy })

expect(body).toEqual({
clientId,
policy,
version: 1,
data: policy,
id: expect.any(String),
createdAt: expect.any(String)
latestSync: {
success: true
}
})
expect(status).toEqual(HttpStatus.CREATED)
})
Expand Down Expand Up @@ -268,11 +270,11 @@ describe('Data Store', () => {
.send({ entity })

expect(body).toEqual({
clientId,
entity,
version: 1,
data: entity,
id: expect.any(String),
createdAt: expect.any(String)
latestSync: {
success: true
}
})
expect(status).toEqual(HttpStatus.CREATED)
})
Expand All @@ -287,18 +289,13 @@ describe('Data Store', () => {
})

describe('POST /data/sync', () => {
beforeEach(async () => {
jest.spyOn(clusterService, 'sync').mockResolvedValue({ ok: true })
})

it('calls the client data store sync', async () => {
const { status, body } = await request(app.getHttpServer())
const { status } = await request(app.getHttpServer())
.post('/data/sync')
.set(REQUEST_HEADER_CLIENT_ID, clientId)
.set(REQUEST_HEADER_CLIENT_SECRET, clientSecret)
.send()

expect(body).toEqual({ ok: true })
expect(status).toEqual(HttpStatus.OK)
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { publicKeySchema } from '@narval/signature'
import { HttpStatus, Injectable, NotFoundException } from '@nestjs/common'
import { ClientService } from '../../../client/core/service/client.service'
import { ClusterService } from '../../../policy-engine/core/service/cluster.service'
import { SetEntityStoreResponse } from '../../http/rest/dto/set-entity-store.dto'
import { EntityDataStoreRepository } from '../../persistence/repository/entity-data-store.repository'
import { SignatureService } from './signature.service'

Expand Down Expand Up @@ -40,13 +41,17 @@ export class EntityDataStoreService extends SignatureService {
date: latestDataStore?.createdAt
})

const dataStore = await this.entitydataStoreRepository.setDataStore(clientId, {
const { data, version } = await this.entitydataStoreRepository.setDataStore(clientId, {
version: latestDataStore?.version ? latestDataStore.version + 1 : 1,
data: EntityStore.parse(payload)
})

await this.clusterService.sync(clientId)
const success = await this.clusterService.sync(clientId)

return dataStore
return SetEntityStoreResponse.parse({
latestSync: { success },
entity: EntityStore.parse(data),
version
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,17 @@ export class PolicyDataStoreService extends SignatureService {
date: latestDataStore?.createdAt
})

const dataStore = await this.policyDataStoreRepository.setDataStore(clientId, {
const { data, version } = await this.policyDataStoreRepository.setDataStore(clientId, {
version: latestDataStore?.version ? latestDataStore.version + 1 : 1,
data: PolicyStore.parse(payload)
})

await this.clusterService.sync(clientId)
const success = await this.clusterService.sync(clientId)

return dataStore
return {
latestSync: { success },
policy: PolicyStore.parse(data),
version
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { EntityDataStoreService } from '../../../core/service/entity-data-store.
import { PolicyDataStoreService } from '../../../core/service/policy-data-store.service'
import { EntityDataStoreDto } from '../dto/entity-data-store.dto'
import { PolicyDataStoreDto } from '../dto/policy-data-store.dto'
import { SetEntityStoreDto } from '../dto/set-entity-store.dto'
import { SetPolicyStoreDto } from '../dto/set-policy-store.dto'
import { SetEntityStoreDto, SetEntityStoreResponseDto } from '../dto/set-entity-store.dto'
import { SetPolicyStoreDto, SetPolicyStoreResponseDto } from '../dto/set-policy-store.dto'

@Controller('/data')
@ApiTags('Managed Data Store')
Expand Down Expand Up @@ -84,7 +84,8 @@ export class DataStoreController {
})
@ApiResponse({
description: 'The client entities have been successfully set',
status: HttpStatus.CREATED
status: HttpStatus.CREATED,
type: SetEntityStoreResponseDto
})
setEntities(@Query('clientId') clientId: string, @Body() body: { entity: SetEntityStoreDto }) {
return this.entityDataStoreService.setEntities(clientId, body.entity)
Expand All @@ -96,7 +97,8 @@ export class DataStoreController {
})
@ApiResponse({
description: 'The client policies have been successfully set',
status: HttpStatus.CREATED
status: HttpStatus.CREATED,
type: SetPolicyStoreResponseDto
})
setPolicies(@Query('clientId') clientId: string, @Body() body: { policy: SetPolicyStoreDto }) {
return this.policyDataStoreService.setPolicies(clientId, body.policy)
Expand All @@ -112,7 +114,21 @@ export class DataStoreController {
description: 'The client data store has been successfully synced',
status: HttpStatus.OK
})
sync(@ClientId('clientId') clientId: string) {
return this.clusterService.sync(clientId)
async sync(@ClientId('clientId') clientId: string) {
try {
const success = await this.clusterService.sync(clientId)

return {
latestSync: {
success
}
}
} catch (error) {
return {
latestSync: {
success: false
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import { EntityStore } from '@narval/policy-engine-shared'
import { createZodDto } from 'nestjs-zod'
import { z } from 'zod'

export const SetEntityStoreResponse = z.object({
entity: EntityStore,
version: z.number(),
latestSync: z.object({
success: z.boolean()
})
})
export type SetEntityStoreResponse = z.infer<typeof SetEntityStoreResponse>

export class SetEntityStoreDto extends createZodDto(EntityStore) {}

export class SetEntityStoreResponseDto extends createZodDto(SetEntityStoreResponse) {}
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import { PolicyStore } from '@narval/policy-engine-shared'
import { createZodDto } from 'nestjs-zod'
import { z } from 'zod'

export const SetPolicyStoreResponse = z.object({
entity: PolicyStore,
version: z.number(),
latestSync: z.object({
success: z.boolean()
})
})
export type SetPolicyStoreResponse = z.infer<typeof SetPolicyStoreResponse>

export class SetPolicyStoreDto extends createZodDto(PolicyStore) {}

export class SetPolicyStoreResponseDto extends createZodDto(SetPolicyStoreResponse) {}
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,9 @@ describe(ClusterService.name, () => {
clientSecret
})

const response = await clusterService.sync(clientId)
const ok = await clusterService.sync(clientId)

expect(response).toEqual(mockResponseOne)
expect({ ok }).toEqual(mockResponseOne)
})

it('throws when client nodes are not found', async () => {
Expand Down
6 changes: 3 additions & 3 deletions apps/armory/src/policy-engine/core/service/cluster.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export class ClusterService {
throw new ClusterNotFoundException(clientId)
}

const responses = await Promise.all(
const responses: { ok: boolean }[] = await Promise.all(
nodes.map((node) =>
this.policyEngineClient.syncClient({
host: node.url,
Expand All @@ -215,8 +215,8 @@ export class ClusterService {
)
)

if (responses.length) {
return responses[0]
if (responses.length && responses.every((response) => response.ok)) {
return true
}

throw new UnreachableClusterException(clientId, nodes)
Expand Down
4 changes: 2 additions & 2 deletions apps/devtool/src/app/_hooks/useAuthServerApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ const useAuthServerApi = () => {
try {
setErrors(undefined)
setIsProcessing(true)
const isSynced = await syncArmoryEngine(sdkAuthClientConfig)
setIsSynced(isSynced)
const { latestSync } = await syncArmoryEngine(sdkAuthClientConfig)
setIsSynced(latestSync.success)
setTimeout(() => setIsSynced(false), 5000)
} catch (error) {
setErrors(extractErrorMessage(error))
Expand Down
4 changes: 2 additions & 2 deletions apps/devtool/src/app/_hooks/useEngineApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ const useEngineApi = () => {
try {
setErrors(undefined)
setIsProcessing(true)
const isSynced = await syncPolicyEngine(sdkEngineClientConfig)
setIsSynced(isSynced)
const { latestSync } = await syncPolicyEngine(sdkEngineClientConfig)
setIsSynced(latestSync.success)
setTimeout(() => setIsSynced(false), 5000)
} catch (error) {
setErrors(extractErrorMessage(error))
Expand Down
2 changes: 1 addition & 1 deletion apps/policy-engine/src/engine/__test__/e2e/client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ describe('Client', () => {
.set(REQUEST_HEADER_CLIENT_SECRET, client.clientSecret)
.send(createClientPayload)

expect(body).toEqual({ ok: true })
expect(body).toEqual({ latestSync: { success: true } })
expect(status).toEqual(HttpStatus.OK)
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,19 @@ export class ClientController {
})
async sync(@ClientId() clientId: string) {
try {
const ok = await this.clientService.syncDataStore(clientId)
const success = await this.clientService.syncDataStore(clientId)

return { ok }
return {
latestSync: {
success
}
}
} catch (error) {
return { ok: false }
return {
latestSync: {
success: false
}
}
}
}
}
4 changes: 2 additions & 2 deletions packages/armory-sdk/src/lib/http/armory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ export const sendAuthorizationRequest = async (
}
}

export const syncArmoryEngine = async (config: AuthClientConfig): Promise<boolean> => {
export const syncArmoryEngine = async (config: AuthClientConfig): Promise<{ latestSync: { success: boolean } }> => {
try {
const { authHost, authClientId: clientId, authClientSecret: clientSecret } = config

if (!clientSecret) {
throw new NarvalSdkException('Client secret is required to sync engine', { config })
}

const { data } = await axios.post(`${authHost}/data/sync`, null, {
const { data } = await axios.post<{ latestSync: { success: boolean } }>(`${authHost}/data/sync`, null, {
headers: builBasicHeaders({ clientId, clientSecret })
})

Expand Down
13 changes: 7 additions & 6 deletions packages/armory-sdk/src/lib/http/data-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { HEADER_CLIENT_ID } from '../constants'
import { DataStoreClientConfig } from '../domain'
import { NarvalSdkException } from '../exceptions'
import { signDataPayload } from '../sdk'
import { SetEntitiesResponse, SetPoliciesResponse } from '../types/data-store'
import { isSuccessResponse } from '../utils'

export const getEntities = async (entityStoreHost: string): Promise<EntityStore> => {
Expand All @@ -30,13 +31,13 @@ export const getPolicies = async (policyStoreHost: string): Promise<PolicyStore>
}
}

export const setEntities = async (config: DataStoreClientConfig, data: Entities): Promise<{ success: boolean }> => {
export const setEntities = async (config: DataStoreClientConfig, data: Entities): Promise<SetEntitiesResponse> => {
try {
const { dataStoreClientId: clientId, entityStoreHost, ...payload } = config

const signature = await signDataPayload({ clientId, ...payload }, data)
const entity = EntityStore.parse({ data, signature })
const response = await axios.post(
const response = await axios.post<SetEntitiesResponse>(
entityStoreHost,
{ entity },
{
Expand All @@ -54,7 +55,7 @@ export const setEntities = async (config: DataStoreClientConfig, data: Entities)
})
}

return { success: true }
return response.data
} catch (error) {
throw new NarvalSdkException('Failed to set entities', {
config,
Expand All @@ -64,13 +65,13 @@ export const setEntities = async (config: DataStoreClientConfig, data: Entities)
}
}

export const setPolicies = async (config: DataStoreClientConfig, data: Policy[]): Promise<{ success: boolean }> => {
export const setPolicies = async (config: DataStoreClientConfig, data: Policy[]): Promise<SetPoliciesResponse> => {
try {
const { dataStoreClientId: clientId, policyStoreHost, ...payload } = config

const signature = await signDataPayload({ clientId, ...payload }, data)
const policy = PolicyStore.parse({ data, signature })
const response = await axios.post(
const response = await axios.post<SetPoliciesResponse>(
policyStoreHost,
{ policy },
{
Expand All @@ -88,7 +89,7 @@ export const setPolicies = async (config: DataStoreClientConfig, data: Policy[])
})
}

return { success: true }
return response.data
} catch (error) {
throw new NarvalSdkException('Failed to set policies', {
config,
Expand Down
6 changes: 3 additions & 3 deletions packages/armory-sdk/src/lib/http/policy-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,19 @@ export const sendEvaluationRequest = async (
}
}

export const syncPolicyEngine = async (config: EngineClientConfig): Promise<boolean> => {
export const syncPolicyEngine = async (config: EngineClientConfig): Promise<{ latestSync: { success: boolean } }> => {
try {
const { engineHost, engineClientId: clientId, engineClientSecret: clientSecret } = config

if (!clientSecret) {
throw new NarvalSdkException('Client secret is required to sync engine', { config })
}

const { data } = await axios.post<{ ok: boolean }>(`${engineHost}/clients/sync`, null, {
const { data } = await axios.post<{ latestSync: { success: boolean } }>(`${engineHost}/clients/sync`, null, {
headers: builBasicHeaders({ clientId, clientSecret })
})

return data.ok
return data
} catch (error) {
throw new NarvalSdkException('Failed to sync engine', { config, error })
}
Expand Down
Loading

0 comments on commit 2fd7fca

Please sign in to comment.