All files / entities delete.js

92.59% Statements 25/27
90% Branches 9/10
50% Functions 1/2
92.59% Lines 25/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 1131x 1x 1x 1x   1x 1x   1x 13x 13x 13x   13x 1x                   12x 1x                     11x                           11x             10x 2x                     8x 2x                   6x             5x                 2x 2x                       1x      
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb')
const { DynamoDBDocumentClient, GetCommand, DeleteCommand } = require('@aws-sdk/lib-dynamodb')
const { requirePermission } = require('../utils/requirePermission')
const { RESERVED_ENTITY_IDS } = require('../utils/constants')
 
const client = new DynamoDBClient({})
const docClient = DynamoDBDocumentClient.from(client)
 
const deleteEntityHandler = async (event) => {
  try {
    const id = event.pathParameters?.entityId
    const tenantId = event.requestContext?.authorizer?.claims?.['custom:tenantId']
 
    if (!tenantId) {
      return {
        statusCode: 401,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({ error: 'Missing tenant context' })
      }
    }
 
    if (!id) {
      return {
        statusCode: 400,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({ error: 'Missing id' })
      }
    }
 
    // Prevent deletion of system entities
    Iif (RESERVED_ENTITY_IDS.includes(id)) {
      return {
        statusCode: 403,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({
          error: `Cannot delete system entity "${id}". System entities are protected.`
        })
      }
    }
 
    // Verify entity exists and ownership
    const result = await docClient.send(
      new GetCommand({
        TableName: process.env.TABLE_NAME,
        Key: { id }
      })
    )
 
    if (!result.Item) {
      return {
        statusCode: 404,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({ error: 'Entity not found' })
      }
    }
 
    // Only owner tenant can delete
    if (result.Item.tenantId !== tenantId) {
      return {
        statusCode: 403,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({ error: 'Access denied - only owner can delete' })
      }
    }
 
    await docClient.send(
      new DeleteCommand({
        TableName: process.env.TABLE_NAME,
        Key: { id }
      })
    )
 
    return {
      statusCode: 204,
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
      },
      body: ''
    }
  } catch (error) {
    console.error('Error:', error)
    return {
      statusCode: 500,
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
      },
      body: JSON.stringify({ error: 'Failed to delete entity' })
    }
  }
}
 
// Wrap with permission check: require 'entity-{id}:delete' permission
exports.handler = requirePermission(deleteEntityHandler, {
  permission: (event) => `entity-${event.pathParameters?.entityId}:delete`
})