Bug fixes and implementation refinement around `ts_ext_data`.
				
					
				
			* Transformed the test database to match the new data model. Added
  ``ts_ext_data`` table and moved ``ts_user.ext_data`` values to it.
* Added D0022: cascade delete ``ts_ext_data`` when ``ts_entry`` record is
  deleted.
* Completed refactor of ``ts_api`` functions to account for extended data:
    * ``post_entry/3``
    * ``put_entry/3``
* Created ``ts_entry:write/2`` to write extended data atomically with the entry.
* Fixed copy/paste bug in ``ts_ext_data:create_table/1``.
* Fixed implementation of ``ts_ext_data:set_property/3`` to be explicit about
  taking a record, not a reference. It then extracts the reference and passes it
  to the underlying implementation in ``ts_ext_data:do_set_property/3``.
* Refactored ``ts_ext_data:do_set_property/3`` to take a record *reference*, not
  the record itself.
* Refactored the ``ts_ext_data:get_property/2`` and
  ``ts_ext_data:get_properties/1`` functions to return key value tuple pairs,
  not ``ts_ext_data{}`` records.
			
			
This commit is contained in:
		
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								db/test/ts_ext_data.DCD
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								db/test/ts_ext_data.DCD
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								db/test/ts_ext_data.DCL
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								db/test/ts_ext_data.DCL
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										10
									
								
								doc/issues/desktop/0022bn5.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								doc/issues/desktop/0022bn5.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | Deleting an entry should cascade delete extended data. | ||||||
|  | ====================================================== | ||||||
|  |  | ||||||
|  | Currently the data remains in the database. It should not cause any  | ||||||
|  | problems, but it is wasting space. | ||||||
|  |  | ||||||
|  | =========  ========== | ||||||
|  | Created:   2011-06-15 | ||||||
|  | Resolved:  YYYY-MM-DD | ||||||
|  | =========  ========== | ||||||
| @@ -418,11 +418,10 @@ post_entry(YArg, Username, TimelineId) -> | |||||||
|             #ts_entry{ref = {Username, TimelineId, undefined}}, EJSON) |             #ts_entry{ref = {Username, TimelineId, undefined}}, EJSON) | ||||||
|          catch _:InputError -> |          catch _:InputError -> | ||||||
|             error_logger:error_report("Bad input: ~p", [InputError]), |             error_logger:error_report("Bad input: ~p", [InputError]), | ||||||
|             throw(make_json_400(YArg)) |             throw(make_json_400(YArg, {request_error, InputError})) | ||||||
|          end, |          end, | ||||||
|  |  | ||||||
|     %% TODO; should entries and their properties be created atomically? |     case ts_entry:new(ER, ExtData) of | ||||||
|     case ts_entry:new(ER) of |  | ||||||
|         % record created |         % record created | ||||||
|         {ok, CreatedRecord} -> |         {ok, CreatedRecord} -> | ||||||
|              |              | ||||||
| @@ -445,14 +444,14 @@ put_entry(YArg, Username, TimelineId, EntryId) -> | |||||||
|     EJSON = parse_json_body(YArg), |     EJSON = parse_json_body(YArg), | ||||||
|  |  | ||||||
|     % parse into ts_entry record |     % parse into ts_entry record | ||||||
|     ER = try ts_json:ejson_to_record_strict( |     {ER, ExtData} = try ts_json:ejson_to_record_strict( | ||||||
|             #ts_entry{ref={Username, TimelineId, EntryId}}, EJSON) |             #ts_entry{ref={Username, TimelineId, EntryId}}, EJSON) | ||||||
|          catch _:InputError -> |          catch _:InputError -> | ||||||
|             error_logger:error_report("Bad input: ~p", [InputError]), |             error_logger:error_report("Bad input: ~p", [InputError]), | ||||||
|             throw(make_json_400(YArg)) |             throw(make_json_400(YArg)) | ||||||
|          end, |          end, | ||||||
|  |  | ||||||
|     ts_entry:write(ER), |     ts_entry:write(ER, ExtData), | ||||||
|     make_json_200(YArg, ER). |     make_json_200(YArg, ER). | ||||||
|  |  | ||||||
| delete_entry(YArg, Username, TimelineId, EntryId) ->  | delete_entry(YArg, Username, TimelineId, EntryId) ->  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| -module(ts_entry). | -module(ts_entry). | ||||||
| -export([create_table/1, new/1, new/2, update/1, update/2, write/1, delete/1, | -export([create_table/1, new/1, new/2, update/1, update/2, write/1, write/2, | ||||||
|          lookup/3, list_asc/3, list_desc/3]). |          delete/1, lookup/3, list_asc/3, list_desc/3]). | ||||||
|  |  | ||||||
| -include("ts_db_records.hrl"). | -include("ts_db_records.hrl"). | ||||||
| -include_lib("stdlib/include/qlc.hrl"). | -include_lib("stdlib/include/qlc.hrl"). | ||||||
| @@ -35,6 +35,13 @@ update(ER = #ts_entry{}, ExtData) when is_list(ExtData) -> | |||||||
|      |      | ||||||
| write(ER = #ts_entry{}) -> mnesia:dirty_write(ER). | write(ER = #ts_entry{}) -> mnesia:dirty_write(ER). | ||||||
|  |  | ||||||
|  | write(ER = #ts_entry{}, ExtData) -> | ||||||
|  |     {atomic, Result} = mnesia:transcation(fun() -> | ||||||
|  |         ok = mnesia:write(ER), | ||||||
|  |         ok = ts_common:do_set_ext_data(ER, ExtData) | ||||||
|  |     end), | ||||||
|  |     Result. | ||||||
|  |  | ||||||
| lookup(Username, TimelineId, EntryId) -> | lookup(Username, TimelineId, EntryId) -> | ||||||
|     case mnesia:dirty_read(ts_entry, {Username, TimelineId, EntryId}) of |     case mnesia:dirty_read(ts_entry, {Username, TimelineId, EntryId}) of | ||||||
|         [] -> no_record; |         [] -> no_record; | ||||||
|   | |||||||
| @@ -4,41 +4,44 @@ | |||||||
| -include("ts_db_records.hrl"). | -include("ts_db_records.hrl"). | ||||||
|  |  | ||||||
| create_table(TableOpts) -> | create_table(TableOpts) -> | ||||||
|     mnesia:create_table(ts_entry, |     mnesia:create_table(ts_ext_data, | ||||||
|         TableOpts ++ [{attributes, record_info(fields, ts_ext_data)}, |         TableOpts ++ [{attributes, record_info(fields, ts_ext_data)}, | ||||||
|                       {type, ordered_set}]). |                       {type, ordered_set}]). | ||||||
|  |  | ||||||
| % set last timeline | % set last timeline | ||||||
| set_property(Ref=#ts_user{}, last_timeline, LastTimelineId) -> | set_property(Rec=#ts_user{}, last_timeline, LastTimelineId) -> | ||||||
|     do_set_property(Ref, last_timeline, LastTimelineId); |     do_set_property(Rec#ts_user.username, last_timeline, LastTimelineId); | ||||||
|  |  | ||||||
| % Set exclusion_list for a User account | % Set exclusion_list for a User account | ||||||
| set_property(Ref=#ts_user{}, entry_exclusions, ExclusionList) -> | set_property(Rec=#ts_user{}, entry_exclusions, ExclusionList) -> | ||||||
|     do_set_property(Ref, entry_exclusions, string:join(ExclusionList, "|")); |     do_set_property(Rec#ts_user.username, entry_exclusions, ExclusionList); | ||||||
|  |  | ||||||
| % Set exclusion_list for a Timeline entry | % Set exclusion_list for a Timeline entry | ||||||
| set_property(Ref=#ts_timeline{}, entry_exclusions, ExclusionList) -> | set_property(Rec=#ts_timeline{}, entry_exclusions, ExclusionList) -> | ||||||
|     do_set_property(Ref, entry_exclusions, string:join(ExclusionList, "|")); |     do_set_property(Rec#ts_timeline.ref, entry_exclusions, ExclusionList); | ||||||
|  |  | ||||||
| set_property(Ref, Key, Value) -> | set_property(Rec, Key, _Value) -> | ||||||
|     throw(io_lib:format("Property '~s' not available for a ~s record.", |     throw(io_lib:format("Property '~s' not available for a ~s record.", | ||||||
|         [Key, element(1, Ref)])). |         [Key, element(1, Rec)])). | ||||||
|  |  | ||||||
| get_property(Ref, PropKey) -> | get_property(Ref, PropKey) -> | ||||||
|     {atomic, Result} = mnesia:transaction(fun() -> |     {atomic, Result} = mnesia:transaction(fun() -> | ||||||
|         case mnesia:read(ts_ext_data, {Ref, PropKey}) of |         case mnesia:read(ts_ext_data, {Ref, PropKey}) of | ||||||
|             [] -> not_set; |             [] -> not_set; | ||||||
|             [Property] -> Property |             [Property] -> Property#ts_ext_data.value | ||||||
|         end |         end | ||||||
|     end), |     end), | ||||||
|     Result. |     Result. | ||||||
|  |  | ||||||
| get_properties(Ref) -> | get_properties(Rec) -> | ||||||
|  |     Ref = element(2, Rec), | ||||||
|     {atomic, Result} = mnesia:transaction(fun() -> |     {atomic, Result} = mnesia:transaction(fun() -> | ||||||
|         MatchHead = #ts_ext_data{ref = {Ref, '_'}, _='_'}, |         MatchHead = #ts_ext_data{ref = {Ref, '_'}, _='_'}, | ||||||
|         mnesia:select(ts_ext_data, [{MatchHead, [], ['$_']}]) |         mnesia:select(ts_ext_data, [{MatchHead, [], ['$_']}]) | ||||||
|     end), |     end), | ||||||
|     Result. |     lists:map(fun(ExtData = #ts_ext_data{}) -> | ||||||
|  |         {Ref, Key} = ExtData#ts_ext_data.ref, | ||||||
|  |         {Key, ExtData#ts_ext_data.value} end, Result). | ||||||
|  |  | ||||||
| do_set_property(Ref, PropKey, Val) -> | do_set_property(Ref, PropKey, Val) -> | ||||||
|     {atomic, Result} = mnesia:transaction(fun() -> |     {atomic, Result} = mnesia:transaction(fun() -> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user