Repository: google/timesketch

Issue:
Sketch attributes deleted via the API cannot be re-added by an analyzer
**Describe the bug**
Deleting a sketch attribute via the notebook will prevent it from being re-added by an analyzer run.

**To Reproduce**
Steps to reproduce the behavior (using the dev container):
1. Add `self.sketch.add_sketch_attribute('test123', ['test'], 'text')` to an analyzer's `run` method.
2. Run the analyzer and confirm attribute has been added via the TimeSketch UI.
3. Using the notebook runtime on port 8844 initialise the sketch object and then run the following code to remove all sketch attributes:
```
for attribute in sketch.attributes:
    sketch.remove_attribute(attribute, 'text')
```
4. Re run the same analyzer and the attribute will not be re-added.

**Expected behavior**
Once the attribute has been deleted running the analyzer should re-create the sketch attribute.

**Desktop (please complete the following information):**
 - Running the dev Docker setup, doesn't appear to be related to Browser/OS environment.

**Additional context**
In https://github.com/google/timesketch/blob/master/timesketch/lib/analyzers/interface.py#L467
```
467        attribute = Attribute.query.filter_by(name=name).first()
```
Is still returning an instance of `timesketch.models.sketch.Attribute` for an attribute that has been deleted via the notebook but will return `None` for an attribute that has never been created.

This seems to suggest an issue at the SQLAlchemy layer.

Sketch attributes deleted via the API cannot be re-added by an analyzer
**Describe the bug**
Deleting a sketch attribute via the notebook will prevent it from being re-added by an analyzer run.

**To Reproduce**
Steps to reproduce the behavior (using the dev container):
1. Add `self.sketch.add_sketch_attribute('test123', ['test'], 'text')` to an analyzer's `run` method.
2. Run the analyzer and confirm attribute has been added via the TimeSketch UI.
3. Using the notebook runtime on port 8844 initialise the sketch object and then run the following code to remove all sketch attributes:
```
for attribute in sketch.attributes:
    sketch.remove_attribute(attribute, 'text')
```
4. Re run the same analyzer and the attribute will not be re-added.

**Expected behavior**
Once the attribute has been deleted running the analyzer should re-create the sketch attribute.

**Desktop (please complete the following information):**
 - Running the dev Docker setup, doesn't appear to be related to Browser/OS environment.

**Additional context**
In https://github.com/google/timesketch/blob/master/timesketch/lib/analyzers/interface.py#L467
```
467        attribute = Attribute.query.filter_by(name=name).first()
```
Is still returning an instance of `timesketch.models.sketch.Attribute` for an attribute that has been deleted via the notebook but will return `None` for an attribute that has never been created.

This seems to suggest an issue at the SQLAlchemy layer.



Discussion:
Discussion on this issue:
```
thanks, I'll take a look. I wonder if there is more than one attribute with the same name?

this will fetch the first filter_by(name=name)

ah, yeah that is a bug

it doesn't filter on the sketch 🤦

it should be something like: Attribute.query.filter_by(sketch=sketch, name=name).first()

```
Discussion on this issue:
```
thanks, I'll take a look. I wonder if there is more than one attribute with the same name?

this will fetch the first filter_by(name=name)

ah, yeah that is a bug

it doesn't filter on the sketch 🤦

it should be something like: Attribute.query.filter_by(sketch=sketch, name=name).first()

```

Resolving patch:
diff --git a/timesketch/lib/analyzers/interface.py b/timesketch/lib/analyzers/interface.py
--- a/timesketch/lib/analyzers/interface.py
+++ b/timesketch/lib/analyzers/interface.py
@@ -464,7 +464,8 @@ def add_sketch_attribute(self, name, values, ontology='text'):
                 data/ontology.yaml.
         """
         # Check first whether the attribute already exists.
-        attribute = Attribute.query.filter_by(name=name).first()
+        attribute = Attribute.query.filter_by(
+          name=name, sketch=self.sql_sketch).first()
 
         if not attribute:
             attribute = Attribute(


Test patch: