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

Patch existing entities in POST /bundle/import #1383

Merged
merged 148 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
0f1924d
[skip ci]
frenchy64 Sep 11, 2023
ed6cec3
[skip ci]
frenchy64 Sep 11, 2023
82323f3
[skip ci]
frenchy64 Sep 11, 2023
0da761d
[skip ci]
frenchy64 Sep 12, 2023
b1f512b
wip
frenchy64 Sep 12, 2023
8336864
wip
frenchy64 Sep 12, 2023
494faf8
refactor
frenchy64 Sep 12, 2023
28e13eb
refactor
frenchy64 Sep 12, 2023
1de8bc9
change
frenchy64 Sep 12, 2023
4991f56
fix
frenchy64 Sep 12, 2023
3209a91
wip
frenchy64 Sep 12, 2023
c8dc049
fix
frenchy64 Sep 12, 2023
7aec741
schema
frenchy64 Sep 12, 2023
7cd8b1f
refactor
frenchy64 Sep 12, 2023
d79ac7a
refactor
frenchy64 Sep 12, 2023
6f366c9
perf
frenchy64 Sep 12, 2023
58f3810
fix
frenchy64 Sep 12, 2023
bac6275
wip
frenchy64 Sep 12, 2023
2530ab5
fresh
frenchy64 Sep 12, 2023
bdaf671
wip
frenchy64 Sep 12, 2023
0612e16
mv
frenchy64 Sep 12, 2023
9c620ee
[skip ci]
frenchy64 Sep 12, 2023
7ae9e3e
[skip ci]
frenchy64 Sep 12, 2023
8f4ba18
fix
frenchy64 Sep 12, 2023
7b241da
keep
frenchy64 Sep 12, 2023
257d46b
fix
frenchy64 Sep 12, 2023
bb53293
rm
frenchy64 Sep 12, 2023
53dcdfa
trim
frenchy64 Sep 12, 2023
58466b3
wip
frenchy64 Sep 12, 2023
55869e3
revert
frenchy64 Sep 12, 2023
3aa29f0
rm
frenchy64 Sep 12, 2023
546eabf
wip
frenchy64 Sep 12, 2023
3bc8b96
less
frenchy64 Sep 12, 2023
792f881
fix
frenchy64 Sep 12, 2023
b4ef3d1
swap
frenchy64 Sep 12, 2023
d579772
wip
frenchy64 Sep 12, 2023
b601fcb
fix test
frenchy64 Sep 12, 2023
b1ca49a
wip
frenchy64 Sep 13, 2023
f466615
wip
frenchy64 Sep 13, 2023
968ab70
wip
frenchy64 Sep 13, 2023
a863f5a
[skip ci]
frenchy64 Sep 13, 2023
51c05d9
[skip ci]
frenchy64 Sep 13, 2023
66d3f47
wip
frenchy64 Sep 13, 2023
6fce2c4
flag
frenchy64 Sep 13, 2023
41b96b8
fix
frenchy64 Sep 13, 2023
07da0dd
fix
frenchy64 Sep 13, 2023
291c51b
fix
frenchy64 Sep 13, 2023
cefda25
try
frenchy64 Sep 13, 2023
f68d9f6
fix
frenchy64 Sep 13, 2023
858583e
fix
frenchy64 Sep 13, 2023
3e5f341
more schemas
frenchy64 Sep 13, 2023
e95f11a
[skip ci] revert
frenchy64 Sep 13, 2023
bf201d5
[skip ci] Revert "[skip ci] revert"
frenchy64 Sep 13, 2023
8441310
merge earlier
frenchy64 Sep 13, 2023
2b6ce18
progress
frenchy64 Sep 13, 2023
2ab792b
closer
frenchy64 Sep 13, 2023
201e275
good
frenchy64 Sep 13, 2023
8eaff08
update test
frenchy64 Sep 14, 2023
9f35ca0
revert
frenchy64 Sep 14, 2023
99bdc8c
prep for partial schemas
frenchy64 Sep 14, 2023
a1839eb
allow partial entities in POST bundle/import
frenchy64 Sep 14, 2023
bcb241f
rm flag
frenchy64 Sep 14, 2023
91e9d82
rm fn validation logic
frenchy64 Sep 14, 2023
a5e918b
resolve transients earlier
frenchy64 Sep 14, 2023
6cc774b
wip
frenchy64 Sep 14, 2023
c4e4dce
wip
frenchy64 Sep 15, 2023
dbb44e2
progress
frenchy64 Sep 15, 2023
3bd3dfc
merged
frenchy64 Sep 15, 2023
d1c3896
exists
frenchy64 Sep 15, 2023
a80cf14
rm workaround
frenchy64 Sep 15, 2023
07ffb88
wip
frenchy64 Sep 15, 2023
d561842
[skip ci]
frenchy64 Sep 15, 2023
7613c58
[skip ci]
frenchy64 Sep 15, 2023
235f49a
test
frenchy64 Sep 15, 2023
0699579
less fixtures
frenchy64 Sep 15, 2023
94b07ff
wip
frenchy64 Sep 15, 2023
c7a7c4b
wip
frenchy64 Sep 15, 2023
322ca56
wip
frenchy64 Sep 15, 2023
a95376b
wip
frenchy64 Sep 15, 2023
11b001b
schemas
frenchy64 Sep 15, 2023
4b14012
[skip ci]
frenchy64 Sep 15, 2023
5f81748
wip
frenchy64 Sep 15, 2023
03f7e30
[skip ci]
frenchy64 Sep 15, 2023
468717b
tests
frenchy64 Sep 15, 2023
f233e22
[skip ci]
frenchy64 Sep 15, 2023
3fc8c0c
wip
frenchy64 Sep 15, 2023
ca0aebe
wip
frenchy64 Sep 15, 2023
7e1890d
[skip ci]
frenchy64 Sep 15, 2023
46273a5
[skip ci]
frenchy64 Sep 15, 2023
54fbe3c
progress
frenchy64 Sep 19, 2023
bec63d0
fix
frenchy64 Sep 19, 2023
579e0d2
doc
frenchy64 Sep 19, 2023
cb77e7d
[skip ci]
frenchy64 Sep 19, 2023
238db82
closer
frenchy64 Sep 19, 2023
4795f49
fix
frenchy64 Sep 19, 2023
6d7d588
tweaks
frenchy64 Sep 19, 2023
1435f9d
fix
frenchy64 Sep 19, 2023
08a3bd4
try
frenchy64 Sep 19, 2023
350b3b8
wip
frenchy64 Sep 19, 2023
d76bfe8
wip
frenchy64 Sep 22, 2023
de37ea7
wip
frenchy64 Sep 22, 2023
bbb9101
[skip ci]
frenchy64 Sep 22, 2023
27e4ff6
wip
frenchy64 Sep 22, 2023
cbebeb2
fixme
frenchy64 Sep 22, 2023
9e12332
revert
frenchy64 Sep 26, 2023
dfde3f8
cleanup
frenchy64 Sep 26, 2023
9237a6c
not an issue
frenchy64 Sep 26, 2023
01115a5
assert
frenchy64 Sep 26, 2023
e9b2fb8
undo
frenchy64 Sep 26, 2023
5d9784a
good
frenchy64 Sep 26, 2023
e7c133a
[skip ci]
frenchy64 Sep 26, 2023
cfabef5
doc
frenchy64 Sep 26, 2023
4ac6415
fix s/=> schema
frenchy64 Sep 27, 2023
b2bd939
second try removing enveloped-result?
frenchy64 Sep 27, 2023
a6fdbb8
Revert "second try removing enveloped-result?"
frenchy64 Sep 27, 2023
4370877
start upsert query param, default true for now
frenchy64 Sep 27, 2023
5d5b0d1
revert all-pages changes
frenchy64 Sep 28, 2023
bd9c9fc
rm old logic
frenchy64 Sep 28, 2023
863e227
introduce non-transient-id?
frenchy64 Sep 28, 2023
8639740
wip query params
frenchy64 Sep 28, 2023
6d30d01
bundle doc
frenchy64 Sep 28, 2023
4502f5a
rename upsert query param => patch-existing
frenchy64 Sep 28, 2023
31b3819
prep merge query param test
frenchy64 Sep 28, 2023
35cba95
bring back old code path
frenchy64 Sep 28, 2023
3397a04
add back old prepare-bulk
frenchy64 Sep 28, 2023
e4b76cd
compile
frenchy64 Sep 28, 2023
0417c69
fix
frenchy64 Sep 29, 2023
3f89ab6
reuse same code path
frenchy64 Sep 29, 2023
26ff698
fix merge strategy test
frenchy64 Sep 29, 2023
90e4fa2
rm prn
frenchy64 Sep 29, 2023
756369f
test patch existing
frenchy64 Sep 29, 2023
2431736
[skip ci]
frenchy64 Sep 29, 2023
e235732
[skip ci]
frenchy64 Sep 29, 2023
3056bb6
fix test
frenchy64 Oct 2, 2023
1390d75
rm
frenchy64 Oct 2, 2023
1218476
attempt to start removing :enveloped-result?
frenchy64 Oct 2, 2023
b788a4a
less :enveloped-result?
frenchy64 Oct 2, 2023
0aa2b95
a little closer
frenchy64 Oct 2, 2023
2c33547
fixed
frenchy64 Oct 4, 2023
afc2391
rm :index schema
frenchy64 Oct 4, 2023
ebeea7d
[skip ci] doc
frenchy64 Oct 4, 2023
2c33891
make result
frenchy64 Oct 4, 2023
3e43607
Revert "rm :index schema"
frenchy64 Oct 5, 2023
8e98c64
refactor
frenchy64 Oct 5, 2023
afe909c
iff
frenchy64 Oct 11, 2023
d91a7c6
doc
frenchy64 Oct 11, 2023
0d9d343
feedback
frenchy64 Oct 11, 2023
c2960b0
doc
frenchy64 Oct 12, 2023
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
6 changes: 4 additions & 2 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@
"git" "symbolic-ref" "--short" "HEAD")))})}]


:profiles {:dev {:dependencies [[puppetlabs/trapperkeeper ~trapperkeeper-version
:profiles {:dev {:jvm-opts ["-XX:-OmitStackTraceInFastThrow"]
:dependencies [[puppetlabs/trapperkeeper ~trapperkeeper-version
:classifier "test"]
[puppetlabs/kitchensink ~trapperkeeper-version
:classifier "test"]
Expand All @@ -228,7 +229,8 @@
:global-vars {*warn-on-reflection* true}
:jvm-opts [ ;; actually print stack traces instead of useless
;; "Full report at: /tmp/clojure-8187773283812483853.edn"
"-Dclojure.main.report=stderr"]}
"-Dclojure.main.report=stderr"
"-XX:-OmitStackTraceInFastThrow"]}
:next-clojure {:dependencies [[org.clojure/clojure "1.12.0-master-SNAPSHOT"]]
:repositories [["snapshots" "https://oss.sonatype.org/content/repositories/snapshots/"]]}
:jmx {:jvm-opts ["-Dcom.sun.management.jmxremote"
Expand Down
12 changes: 10 additions & 2 deletions resources/ctia/public/doc/bulk-bundle.org
Original file line number Diff line number Diff line change
Expand Up @@ -667,10 +667,18 @@ When a bundle is submitted:

1. All entities that have already been imported with the external ID whose prefix has been configured with the `ctia.store.external-key-prefixes` property are searched.
2. If they are identified by transient IDs, a mapping table between transient and stored IDs is built.
3. Only new entities are created in the same way as the `/bulk` API endpoint with transient IDs resolutions. Existing entities are not modified.
3. New entities are created in the same way as the `/bulk` API endpoint with transient IDs and external ids resolutions. If query parameter `patch-existing=true`, then existing entities are similarly patched with `result=updated`; otherwise, existing entities are not modified with `result=exists`.

If more than one entity is referenced by the same external ID, an error is reported.

If provided query parameter `asset_properties-merge-strategy=merge-overriding-previous`, then when patching asset properties,
frenchy64 marked this conversation as resolved.
Show resolved Hide resolved
existing asset properties will be retrieved and combined with the asset properties in the request bundle
as if by concatenating existing and new properties together in a single list,
removing properties to the left of a property with the same name,
then sorting the list lexicographically by name before using this list to patch the existing entity.
frenchy64 marked this conversation as resolved.
Show resolved Hide resolved

If `asset_properties-merge-strategy=ignore-existing`, then asset properties will be patched to their new values as they appear in the request bundle.

Example for an incident along with its context, note the transient ids
#+begin_src HTTP
POST /ctia/bundle/import HTTP/1.1
Expand Down Expand Up @@ -778,7 +786,7 @@ accept: application/json
|----------------+----------------------------------------------------------|
| `:id` | The real ID |
| `:original_id` | Provided ID if different from real ID (ex: transient ID) |
| `:result` | `error`, `created` or `exists` |
| `:result` | `error`, `created`, `exists` or `updated` |
| `:external_id` | External ID used to identify the entity |
| `:error` | Error message |

Expand Down
5 changes: 4 additions & 1 deletion src/ctia/auth.clj
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,16 @@

(def denied-identity-singleton (->DeniedIdentity))

(s/defschema AuthIdentity
(s/protocol IIdentity))

(s/defschema IdentityMap
{:client-id (s/maybe s/Str)
:login (s/maybe s/Str)
:groups [s/Str]})

(s/defn ident->map :- (s/maybe IdentityMap)
[ident]
[ident :- (s/maybe AuthIdentity)]
(when ident
{:login (login ident)
:groups (groups ident)
Expand Down
154 changes: 117 additions & 37 deletions src/ctia/bulk/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,47 @@

(s/defn create-fn
"return the create function provided an entity type key"
[k auth-identity params
[k
auth-identity :- auth/AuthIdentity
params
{{:keys [get-store]} :StoreService} :- APIHandlerServices]
#(-> (get-store k)
(store/create-record
%
(auth/ident->map auth-identity)
params)))

(s/defn create-entities
(s/defschema EntitiesResult
[(s/conditional
string? schemas/ID
:else {(s/optional-key :error) (s/conditional
string? s/Str
:else {:type (s/conditional
string? s/Str
:else s/Keyword)
:reason s/Str
(s/optional-key :index) s/Str
(s/optional-key :index_uuid) s/Str})
frenchy64 marked this conversation as resolved.
Show resolved Hide resolved
(s/optional-key :msg) s/Str
(s/optional-key :entity) (s/pred map?)
(s/optional-key :type) (s/conditional
string? s/Str
:else s/Keyword)
(s/optional-key :id) (s/maybe s/Str)
s/Keyword s/Any})])

(s/defschema EnvelopedEntities+TempIDs
(s/maybe
{:data EntitiesResult
(s/optional-key :tempids) TempIDs}))

(s/defn create-entities :- EnvelopedEntities+TempIDs
"Create many entities provided their type and returns a list of ids"
[new-entities entity-type tempids auth-identity params
[new-entities :- flows/Entities
entity-type :- s/Keyword
tempids :- TempIDs
auth-identity :- auth/AuthIdentity
params
services :- APIHandlerServices]
(when (seq new-entities)
(let [{:keys [realize-fn new-spec]} (get (all-entities) entity-type)]
Expand Down Expand Up @@ -82,9 +112,11 @@
(st/assoc s/Keyword s/Any))
s/Keyword s/Any})

(s/defn read-entities
(s/defn read-entities :- [(s/maybe (s/pred map?))]
"Retrieve many entities of the same type provided their ids and common type"
[ids entity-type auth-identity
[ids :- [s/Str]
entity-type :- s/Keyword
auth-identity :- auth/AuthIdentity
{{:keys [get-store]} :StoreService
:as services} :- ReadEntitiesServices]
(let [store (get-store entity-type)]
Expand Down Expand Up @@ -125,9 +157,19 @@
concat
(map #(to-long-id % services) not-found)))))

(defn make-patch-bulk-enveloped-result
[fm]
(-> fm
flows/make-enveloped-result
(update :data #(mapv (fn [{:keys [error id] :as result}]
(if error result id))
%))))

(s/defn delete-fn
"return the delete function provided an entity type key"
[k auth-identity params
[k
auth-identity :- auth/AuthIdentity
params
{{:keys [get-store]} :StoreService} :- APIHandlerServices]
#(-> (get-store k)
(store/bulk-delete
Expand All @@ -137,7 +179,9 @@

(s/defn update-fn
"return the update function provided an entity type key"
[k auth-identity params
[k
auth-identity :- auth/AuthIdentity
params
{{:keys [get-store]} :StoreService} :- APIHandlerServices]
#(-> (get-store k)
(store/bulk-update
Expand All @@ -159,7 +203,10 @@

(s/defn delete-entities
"delete many entities provided their type and returns a list of ids"
[entity-ids entity-type auth-identity params
[entity-ids
entity-type
auth-identity :- auth/AuthIdentity
params
services :- APIHandlerServices]
(when (seq entity-ids)
(let [get-fn #(read-entities % entity-type auth-identity services)]
Expand All @@ -176,7 +223,10 @@

(s/defn update-entities
"update many entities provided their type and returns errored and successed entities' ids"
[entities entity-type auth-identity params
[entities
entity-type
auth-identity :- auth/AuthIdentity
params
services :- APIHandlerServices]
(when (seq entities)
(let [get-fn #(read-entities % entity-type auth-identity services)
Expand All @@ -196,24 +246,32 @@

(s/defn patch-entities
"patch many entities provided their type and returns errored and successed entities' ids"
[patches entity-type auth-identity params
services :- APIHandlerServices]
[patches
entity-type
tempids :- TempIDs
auth-identity :- auth/AuthIdentity
params
services :- APIHandlerServices
{:keys [make-result]} :- {(s/optional-key :make-result) s/Any}]
(when (seq patches)
(let [get-fn #(read-entities % entity-type auth-identity services)
{:keys [realize-fn new-spec]} (get (all-entities) entity-type)]
(flows/patch-flow
:services services
:get-fn get-fn
:realize-fn realize-fn
:update-fn (update-fn entity-type auth-identity params services)
:long-id-fn #(with-long-id % services)
:entity-type entity-type
:identity auth-identity
:patch-operation :replace
:partial-entities patches
:spec new-spec
:make-result make-bulk-result
:get-success-entities (get-success-entities-fn :updated)))))
:services services
:get-fn get-fn
:realize-fn realize-fn
:update-fn (update-fn entity-type auth-identity params services)
:long-id-fn #(with-long-id % services)
:entity-type entity-type
:identity auth-identity
:patch-operation :replace
:partial-entities patches
:tempids tempids
:spec new-spec
:make-result (or make-result make-bulk-result)
:get-success-entities (get-success-entities-fn :updated)))))

(s/defschema BulkEntities {s/Keyword flows/Entities})

(defn gen-bulk-from-fn
"Kind of fmap but adapted for bulk
Expand All @@ -237,7 +295,7 @@
(catch java.util.concurrent.ExecutionException e
(throw (.getCause e)))))

(defn merge-tempids
(s/defn merge-tempids :- TempIDs
"Merges tempids from all entities
{:entity-type1 {:data []
:tempids {transientid1 id1
Expand All @@ -255,7 +313,7 @@
The create-entities set the enveloped-result? to True in the flow
configuration to get :data and :tempids for each entity in the result."
[entities-by-type]
[entities-by-type :- {s/Keyword EnvelopedEntities+TempIDs}]
(into {}
(map (fn [[_ v]] (:tempids v)))
entities-by-type))
Expand All @@ -278,15 +336,19 @@
(bad-request! (str "Bulk max number of entities: "
(get-bulk-max-size get-in-config)))))

(s/defschema BulkRefs {s/Keyword EntitiesResult})

(s/defschema BulkRefs+TempIDs
{:bulk-refs {s/Keyword [s/Any]}
{:bulk-refs BulkRefs
:tempids TempIDs})

(s/defschema BulkRefsAssocTempIDs
(st/assoc BulkRefs (s/optional-key :tempids) TempIDs))

(s/defn import-bulks-with :- BulkRefs+TempIDs
"Import each new-bulk in order while accumulating tempids."
[f :- (s/=> {s/Keyword {:data [s/Any]
:tempids TempIDs}}
(s/named (s/pred map?) 'new-bulk)
[f :- (s/=> {s/Keyword EnvelopedEntities+TempIDs}
BulkEntities
TempIDs)
new-bulks
tempids :- TempIDs]
Expand All @@ -300,15 +362,15 @@
:tempids tempids}
new-bulks))

(s/defn create-bulk
(s/defn create-bulk :- BulkRefsAssocTempIDs
"Creates entities in bulk. To define relationships between entities,
transient IDs can be used. They are automatically converted into
real IDs.
1. Creates all entities except Relationships
2. Creates Relationships with mapping between transient and real IDs"
([new-bulk login services :- APIHandlerServices] (create-bulk new-bulk {} login {} services))
([new-bulk
([new-bulk :- BulkEntities
tempids :- TempIDs
login
params
Expand All @@ -334,22 +396,40 @@
(seq tempids) (assoc :tempids tempids)))))

(s/defn fetch-bulk
[bulk auth-identity
[bulk
auth-identity :- auth/AuthIdentity
services :- APIHandlerServices]
(ent/un-store-map
(gen-bulk-from-fn read-entities bulk auth-identity services)))

(s/defn delete-bulk
[bulk auth-identity params
[bulk
auth-identity :- auth/AuthIdentity
params
services :- APIHandlerServices]
(gen-bulk-from-fn delete-entities bulk auth-identity params services))

(s/defn update-bulk
[bulk auth-identity params
[bulk
auth-identity :- auth/AuthIdentity
params
services :- APIHandlerServices]
(gen-bulk-from-fn update-entities bulk auth-identity params services))

(s/defn patch-bulk
[bulk auth-identity params
services :- APIHandlerServices]
(gen-bulk-from-fn patch-entities bulk auth-identity params services))
([bulk
tempids :- TempIDs
auth-identity :- auth/AuthIdentity
params
services :- APIHandlerServices]
(patch-bulk bulk tempids auth-identity params services {}))
([bulk
tempids :- TempIDs
auth-identity :- auth/AuthIdentity
params
services :- APIHandlerServices
{:keys [enveloped-result?] :as opts}]
(let [entities (gen-bulk-from-fn patch-entities bulk tempids auth-identity params services (select-keys opts [:make-result]))]
(cond-> entities
enveloped-result? (-> (update-vals :data)
(assoc :tempids (into tempids (merge-tempids entities))))))))
1 change: 1 addition & 0 deletions src/ctia/bulk/routes.clj
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
:auth-identity auth-identity
(core/validate-bulk-size! bulk services)
(ok (core/patch-bulk bulk
{} ;; transient ids only supported via PATCH bundle/import
auth-identity
(common/wait_for->refresh wait_for)
services))))
Expand Down
Loading
Loading